<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Andy Balaam&#x27;s Blog</title>
      <link>https://artificialworlds.net/blog</link>
      <description>Coding in Rust and others; making coding videos.</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://artificialworlds.net/blog/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Fri, 10 Jul 2026 12:00:00 +0000</lastBuildDate>
      <item>
          <title>A full (slow) solution to the Billion Row Challenge</title>
          <pubDate>Fri, 10 Jul 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/07/10/billion-bug/</link>
          <guid>https://artificialworlds.net/blog/2026/07/10/billion-bug/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/07/10/billion-bug/">&lt;p&gt;We&#x27;ve been picking off small sub-tasks of the Billion Row Challenge, which asks
us to summarise a large amount of data as quickly as we can. This time we
complete the whole challenge, but in a slow way, to make sure we understand it.
In future videos we will attack the last remaining sub-tasks, which concern how
to collect together the information (probably in a hashmap) and print a summary.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;A full (slow) solution to the Billion Row Challenge&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;hzreVwHv7hETKUG82KtGGd&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;The code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;brrmbrrm&quot;&gt;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;brrmbrrm&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The billion row challenge: do we have a bug?</title>
          <pubDate>Fri, 19 Jun 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/06/19/billion-bug/</link>
          <guid>https://artificialworlds.net/blog/2026/06/19/billion-bug/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/06/19/billion-bug/">&lt;p&gt;A couple of people contacted me with feedback about the SIMD implementation we
used to find newlines in the billion-row file. One suggested a possible bug
(shock!) and one suggested a way that might be more efficient. We&#x27;ll take a look
at both, obviously starting by writing a test that checks for the bug.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;The billion row challenge: do we have a bug?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;51c81eUAvGdjdL338bcpUZ&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;After the stream I made another attempt at using the information about all
newlines in a 64-byte chunk, instead of just the first one. I did it with no
&lt;code&gt;Vec&lt;&#x2F;code&gt;s at all, unifying the two functions we worked with into a single one with
nested loops. Surprisingly (to me) this was still slower than the original
solution. Again, this seems to prove the power of simplicity! You can find this
code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;brrmbrrm&#x2F;src&#x2F;branch&#x2F;main&#x2F;src&#x2F;read_lines&#x2F;memmap_simd.rs#L152&quot;&gt;memmap_simd.rs#L152&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Writing a bit-wise iterator (with Jez!) for our gunzip implementation</title>
          <pubDate>Fri, 12 Jun 2026 08:11:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/06/12/biterator/</link>
          <guid>https://artificialworlds.net/blog/2026/06/12/biterator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/06/12/biterator/">&lt;p&gt;More Jez+Andy coding, working on our gunzip clone. Jez figured out we need to
iterate bit by bit through the input, so we write a &lt;code&gt;Biterator&lt;&#x2F;code&gt; to do that.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Writing a bit-wise iterator (with Jez!) for our gunzip implementation&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;sygBeAp3iwqRdXdmwRsiha&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;The code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;ggunzip&quot;&gt;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;ggunzip&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Read Jez&#x27;s blog at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;&quot;&gt;https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Follow Jez on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.me.uk&#x2F;@jezhiggins&quot;&gt;@jezhiggins@mastodon.me.uk&lt;&#x2F;a&gt;
Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Keep the faith</title>
          <pubDate>Wed, 10 Jun 2026 13:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/06/10/keep-the-faith/</link>
          <guid>https://artificialworlds.net/blog/2026/06/10/keep-the-faith/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/06/10/keep-the-faith/">&lt;p&gt;This is mainly a message to myself. I have spent the last few weeks feeling more
and more depressed about &quot;the tech industry&quot;. I am sad that social media
companies are manipulating us, making us angry and ignorant. I am sad that AI is
becoming more pervasive when I feel it is
&lt;a href=&quot;&#x2F;blog&#x2F;2025&#x2F;12&#x2F;16&#x2F;why-i-dont-use-ai&#x2F;&quot;&gt;fundamentally unethical&lt;&#x2F;a&gt;. I am worried
that I may lose my job if I don&#x27;t use AI, or feel pressured to use it. I am
worried that the craft to which I have dedicated my life, and which I love,
(programming) is going to die.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;post-truth-and-the-social-contract&quot;&gt;Post-truth and the social contract&lt;&#x2F;h2&gt;
&lt;p&gt;Much of what is happening is so-called &quot;post-truth&quot;. It seems that it doesn&#x27;t
matter what you do any more, so long as you appear to be confident and
aggressive about it. I see this in the political discourse in the UK and the US,
but I also see it elsewhere - for example in the &quot;quiet quitting&quot; phenomenon:
people giving up on the social contract and choosing to take what they can get
from an economy they consider fundamentally opposed to their interests.&lt;&#x2F;p&gt;
&lt;p&gt;AI is the natural extension of post-truth: it confidently spouts opinions,
making no distinction between truth and lies. It also massively enables faking
of all kinds, making it easier to lie. The companies selling this technology
take no responsibility for either of these problems, instead filling the air
with bluster about how important and dangerous it is.&lt;&#x2F;p&gt;
&lt;p&gt;I have always been interested in free and open source software, and my interest
is rooted in the good that it can do: the idea that we can build a better
society by working together. For this to make sense, I need to believe in the
social contract: that others are acting in good faith. I don&#x27;t think I need to
believe that &lt;em&gt;everyone&lt;&#x2F;em&gt; is acting in good faith, but that some critical mass is
doing so.&lt;&#x2F;p&gt;
&lt;p&gt;I realised yesterday that there are some reasons for hope. I hope and trust that
our society will make its way out of the post-truth world into something
better. Here are my reasons for that hope.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-has-happened-before&quot;&gt;This has happened before&lt;&#x2F;h2&gt;
&lt;p&gt;When new information technologies were developed in the past, waves of
disinformation often followed:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;disa.org&#x2F;a-concise-history-of-disinformation&#x2F;&quot;&gt;pamphlets full of lies&lt;&#x2F;a&gt;
became very popular after the printing press, and some have even suggested that
the wars of the 20th century were exacerbated by the availability of mass media
like radio.&lt;&#x2F;p&gt;
&lt;p&gt;Our societies went through awful convulsions, but we found a way back to a
shared understanding of ourselves, and a basic trust in each other.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;people-hate-being-lied-to&quot;&gt;People hate being lied to&lt;&#x2F;h2&gt;
&lt;p&gt;Just this. People &lt;em&gt;really&lt;&#x2F;em&gt; hate being lied to. So I hope and trust that when
lies and corruption are exposed, such as when we find out about all the powerful
people linked to Epstein&#x27;s people trafficking and rape business, we will
eventually (eventually) react by changing our rules and punishing the guilty. I
know there is not a lot of evidence for this claim, specifically in the Epstein
example, but I still believe that liars are unpopular - people just need time to
admit that they were fooled.&lt;&#x2F;p&gt;
&lt;p&gt;All the nasty social media influencers and hateful politicians collapse under
the weight of their own lies. It just takes time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-can-t-deny-gravity&quot;&gt;You can&#x27;t deny gravity&lt;&#x2F;h2&gt;
&lt;p&gt;Bubbles eventually burst: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Enron&quot;&gt;Enron&lt;&#x2F;a&gt; managed
to survive for a long time, but in the end, the law of gravity won, and the
truth became clear.&lt;&#x2F;p&gt;
&lt;p&gt;Invading a country, claiming victory, and then claiming every week that a peace
has been agreed, eventually gets tired. The evidence just mounts up that you are
telling lies. Very soon, most people will settle on the consensus that the
US-Israel war on Iran was a terrible, morally bankrupt, mistake.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes the truth is surprising: it turns out there &lt;em&gt;was&lt;&#x2F;em&gt; a group of elite
paedophiles preying on our children, despite how crazy that sounded, and despite
the details of the conspiracy theory being totally wrong. But the point is that
it was exposed.&lt;&#x2F;p&gt;
&lt;p&gt;The parts of the AI revolution that are simply lies will also be exposed: the
worst of these is the idea that computers can replace humans as friends, but
there are many others.&lt;&#x2F;p&gt;
&lt;p&gt;I keep &quot;early 2027&quot; in my mind, because that is when influential bloggers Scott
Alexander and Daniel Kokotajlo
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ai-2027.com&#x2F;&quot;&gt;predicted an intelligence explosion&lt;&#x2F;a&gt;
that would be undeniable. So far, I see little sign of that, and I look forward
to asking the question at the time.&lt;&#x2F;p&gt;
&lt;p&gt;Social media bubbles make us less empathic and worse informed: I hope and trust
that the truth of this will become obvious, and we will find a better way,
though I can&#x27;t imagine what it will look like.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keep-the-faith&quot;&gt;Keep the faith&lt;&#x2F;h2&gt;
&lt;p&gt;We are still people, and the overwhelming majority of us want to live a world
where we can trust each other. We will work it out. There may be horrible
convulsions, but we will work it out&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Keep the faith. My evidence is admittedly pretty shaky, but that&#x27;s why they call
it faith.&lt;&#x2F;p&gt;
&lt;p&gt;Keep the faith. Don&#x27;t give up.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Yes, climate disaster is the shadow behind everything I&#x27;m saying. I am
certainly not claiming that we will survive climate disaster, or that it will be
good - I&#x27;m just claiming that we will rebuild some sense of trust and
co-operation, assuming we have time.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Benedikt
    &lt;span class=&quot;comment_date&quot;&gt;2026-07-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for this, it&#x27;s hard to keep hoping for a better future despite seemingly
overwhelming evidence to the contrary. Maybe we just have to play the long game.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I really appreciate this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    dapr
    &lt;span class=&quot;comment_date&quot;&gt;2026-07-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&amp;lt;3&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bill McMellon
    &lt;span class=&quot;comment_date&quot;&gt;2026-07-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for this, Andy (your dad has forwarded it to me, or else I would not have
been aware). I am a Quaker, and we are committed to truth. I don&#x27;t think we have
any choice about that in this world, Quaker or not. However deep the lies, and
however much people wish to believe in them, truth will out. There will always
be problems of interpretation (how I express my truth may be different to how
you express yours) but as I say, we have no choice but to honestly work towards
the truth as best we can. We can take no responsibility for other people&#x27;s lies
except to try to expose them. The technology and the billionaires make no
difference except to make the job harder. But here we are, working day to day to
live in the actual world without allowing ourselves to be distracted by the
virtual, imaginary one, except where it coincides with truth. Best Wishes and
Good Luck, Bill.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Implementing gunzip from scratch in Rust</title>
          <pubDate>Fri, 15 May 2026 08:11:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/05/15/jez-ggunzip/</link>
          <guid>https://artificialworlds.net/blog/2026/05/15/jez-ggunzip/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/05/15/jez-ggunzip/">&lt;p&gt;Jez and Andy get together for crazy programming hijinks, trying to figure out
from scratch how gzip compressions works, starting by making a valid minimal
gzipped file, then trying to unzip it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Implementing gunzip from scratch in Rust&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;eguNrw31jx6yNTB7KVskaA&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Read Jez&#x27;s blog at https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;
Read my blog at https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&lt;&#x2F;p&gt;
&lt;p&gt;Follow Jez on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.me.uk&#x2F;@jezhiggins&quot;&gt;@jezhiggins@mastodon.me.uk&lt;&#x2F;a&gt;
Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Jez returns for programming language chat!</title>
          <pubDate>Fri, 08 May 2026 08:11:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/05/08/jez-cell/</link>
          <guid>https://artificialworlds.net/blog/2026/05/08/jez-cell/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/05/08/jez-cell/">&lt;p&gt;Veteran super-coder Jez is back, and claims he has &quot;finished&quot; implementing Cell,
Andy&#x27;s toy language, in Rust. Jez and Andy basically gossip a bit, about code
type stuff.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;jez-cell&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ddh8hoaL2qJj4hvXgS9RUm&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Read Jez&#x27;s blog at https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;
Read my blog at https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&lt;&#x2F;p&gt;
&lt;p&gt;Follow Jez on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.me.uk&#x2F;@jezhiggins&quot;&gt;@jezhiggins@mastodon.me.uk&lt;&#x2F;a&gt;
Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Announcing Changelog Builder - a towncrier clone for building changelogs</title>
          <pubDate>Tue, 05 May 2026 11:59:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/05/05/announcing-changelog-builder/</link>
          <guid>https://artificialworlds.net/blog/2026/05/05/announcing-changelog-builder/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/05/05/announcing-changelog-builder/">&lt;p&gt;I really enjoy re-implementing things that already exist. Sometimes it&#x27;s just
out of interest, sometimes it&#x27;s to make it faster, or work in a different way,
or improve the code quality, but I love having that concrete expectation of what
the code does to work from.&lt;&#x2F;p&gt;
&lt;p&gt;So, when a colleague suggested we might use
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;twisted&#x2F;towncrier&quot;&gt;towncrier&lt;&#x2F;a&gt; to handle our release notes,
but I knew its Python implementation might be tricky to deploy, I saw an
opportunity to make a little Rust version.&lt;&#x2F;p&gt;
&lt;p&gt;Thus &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;changelog-builder&quot;&gt;Changelog Builder&lt;&#x2F;a&gt; was
born! It&#x27;s an implementation of a small subset of what the awesome &lt;code&gt;towncrier&lt;&#x2F;code&gt;
project can do, but it&#x27;s convenient to use in a Rust project because you can
type:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;cargo install changelog-builder
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and it&#x27;s ready to use.&lt;&#x2F;p&gt;
&lt;p&gt;It has a whole suite of tests that check it behaves like &lt;code&gt;towncrier&lt;&#x2F;code&gt; in lots of
different circumstances, but it&#x27;s brand new so I&#x27;ve probably missed lots of
obvious things.&lt;&#x2F;p&gt;
&lt;p&gt;Feedback and PRs welcome!&lt;&#x2F;p&gt;
&lt;p&gt;Repo: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;changelog-builder&quot;&gt;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;changelog-builder&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using anyhow to handle errors in my Rust changelog builder</title>
          <pubDate>Fri, 01 May 2026 08:11:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/05/01/error-handling/</link>
          <guid>https://artificialworlds.net/blog/2026/05/01/error-handling/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/05/01/error-handling/">&lt;p&gt;Let&#x27;s fix our messy error handling in our Rust &lt;code&gt;towncrier&lt;&#x2F;code&gt; clone by using the
&lt;code&gt;anyhow&lt;&#x2F;code&gt; library and adding &lt;code&gt;context()&lt;&#x2F;code&gt; calls to describe what went wrong. Can
we match the output of &lt;code&gt;towncrier&lt;&#x2F;code&gt; in different circumstances? Do we actually
want to match it exactly, or can we do better?&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Using anyhow to handle errors in my Rust changelog builder&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;vhHv6Ns7wGpUt1MZyN6KGW&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the code at https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;changelog-builder&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A simpler blog comment system</title>
          <pubDate>Sun, 26 Apr 2026 08:19:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/04/26/simpler-blog-comment-system/</link>
          <guid>https://artificialworlds.net/blog/2026/04/26/simpler-blog-comment-system/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/04/26/simpler-blog-comment-system/">&lt;p&gt;Since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;10&#x2F;22&#x2F;19-years-of-blogging&#x2F;&quot;&gt;I made this blog statically-generated&lt;&#x2F;a&gt;
using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; I&#x27;ve been using
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;remark42.com&#x2F;&quot;&gt;Remark42&lt;&#x2F;a&gt; as my comment system. I wanted something
self-hosted and simple, and it worked.&lt;&#x2F;p&gt;
&lt;p&gt;These days I get a comment every couple of months, and about half of them are
spam. Remark42 seemed to forget who I was each time, so deleting the spam
involved me remembering where Remark42 was deployed, editing and re-uploading a
config file each time just to become an admin user to delete the comment.&lt;&#x2F;p&gt;
&lt;p&gt;It occurred to me the other day that it would be easier if comments were just
emailed to me and I could add them manually to my blog.&lt;&#x2F;p&gt;
&lt;p&gt;So this weekend I&#x27;ve implemented that with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;artificialworlds.net-blog&#x2F;src&#x2F;branch&#x2F;main&#x2F;templates&#x2F;shortcodes&#x2F;comment.md&quot;&gt;a simple
shortcode&lt;&#x2F;a&gt;
in my Zola code, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;artificialworlds.net-blog&#x2F;src&#x2F;branch&#x2F;main&#x2F;static&#x2F;actions&#x2F;blogcomment.php&quot;&gt;a little sprinkle of
PHP&lt;&#x2F;a&gt;,
and a form at the bottom of the page that just emails me your comment. I will
then check it for spam and add it to the page at my leisure.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s simpler, and it makes clear something that was always true: comments are a
part of my web site, and I curate them for relevance, spamminess and politeness.&lt;&#x2F;p&gt;
&lt;p&gt;The hardest part was extracting Remark42&#x27;s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;boltdb&#x2F;bolt&quot;&gt;boltdb&lt;&#x2F;a&gt; database and doing some extreme
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jqlang&#x2F;jq&quot;&gt;jq&lt;&#x2F;a&gt; to munge the comments into the right format.
That was fun.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to comment on this page, and let me know whether it works!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jez
    &lt;span class=&quot;comment_date&quot;&gt;2026-04-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;tap&lt;&#x2F;em&gt; &lt;em&gt;tap&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Is this thing on?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Benedikt
    &lt;span class=&quot;comment_date&quot;&gt;2026-04-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I just started using this: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kevquirk&#x2F;purecomments&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;kevquirk&#x2F;purecomments&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It might be overkill for you but I thought I&#x27;d a least mention it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy
    &lt;span class=&quot;comment_date&quot;&gt;2026-04-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Benedikt - sounds good but I think I&#x27;m happy with the very-manual approach for now!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Building a Rust commandline tool: a towncrier clone</title>
          <pubDate>Fri, 24 Apr 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/04/24/changelog-builder-minimal/</link>
          <guid>https://artificialworlds.net/blog/2026/04/24/changelog-builder-minimal/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/04/24/changelog-builder-minimal/">&lt;p&gt;Enthusiastically embracing test-driven development as always, we attempt to
write a minimal clone of a useful tool, towncrier, which allows us to build
changelogs out of individual &quot;newsfragment&quot; files. By the end we have the
outline of a decent solution that passes our first and only testcase!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Building a Rust commandline tool: a towncrier clone&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;gZNwW1ELqYBU1aNpDuCvi5&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the code at https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;changelog-builder&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The billion row challenge: splitting lines using SIMD in Rust</title>
          <pubDate>Fri, 17 Apr 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/04/17/billion-splitting-lines/</link>
          <guid>https://artificialworlds.net/blog/2026/04/17/billion-splitting-lines/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/04/17/billion-splitting-lines/">&lt;p&gt;I was scared to do it, but it was actually fine! This time we look at the
quickest way to split up massive text files into separate lines, first by
implementing our own zero-copy version of &lt;code&gt;BufReader::lines&lt;&#x2F;code&gt; and later using
SIMD, which allows us to process 64 bytes simultaneously, using a single CPU
instruction! I&#x27;m pretty sure this is the first time we&#x27;ve ventured into
&lt;code&gt;nightly&lt;&#x2F;code&gt; Rust on this channel...&lt;&#x2F;p&gt;
&lt;iframe title=&quot;The billion row challenge: splitting lines using SIMD in Rust&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;oyJg67E2P8HfuAcxPDboSm&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust: reading very large files for the billion row challenge</title>
          <pubDate>Fri, 10 Apr 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/04/10/billion-read-file/</link>
          <guid>https://artificialworlds.net/blog/2026/04/10/billion-read-file/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/04/10/billion-read-file/">&lt;p&gt;Checking out whether memmap can help us read very large files as fast as
possible, and wondering how &lt;code&gt;wc&lt;&#x2F;code&gt; manages to be so fast. This is the next bit of
the Billion Row Challenge, and probably the closest part to black magic.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust: reading very large files for the billion row challenge&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;hC32rdos46nZkXCtYvkGzi&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Parsing whole lines of the Billion Row Challenge</title>
          <pubDate>Fri, 03 Apr 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/04/03/billion-parsing-lines/</link>
          <guid>https://artificialworlds.net/blog/2026/04/03/billion-parsing-lines/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/04/03/billion-parsing-lines/">&lt;p&gt;Excitingly, our temperature parser can be trivially extended to find the start
of the temperature, so we can use it to parse the whole line!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;billion-parsing-lines&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;rxF6huWj4RahRP3gsb2BU1&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Improving our billion-row parsers and benchmarks</title>
          <pubDate>Fri, 27 Mar 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/03/27/billion-improving-benchmarks/</link>
          <guid>https://artificialworlds.net/blog/2026/03/27/billion-improving-benchmarks/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/03/27/billion-improving-benchmarks/">&lt;p&gt;Improving the benchmarks of our Billion Row Challenge, and playing with some
improved implementations.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;billion-improving-benchmarks&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;mjqCDN6XfG4mHLVULUvTnK&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Making our own String type in Rust</title>
          <pubDate>Fri, 20 Mar 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/03/20/asciistring/</link>
          <guid>https://artificialworlds.net/blog/2026/03/20/asciistring/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/03/20/asciistring/">&lt;p&gt;Implementing our own versions of standard types can help us understand how they
work a bit better. This time we&#x27;ll make a new string type called AsciiString,
and make it handle all its memory itself. Lots of unsafe, and probably lots of
bugs!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Making our own String type in Rust&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;eee8J39WvsgE5Bu8Mskatr&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Writing a Snake game in Rust&#x2F;WASM</title>
          <pubDate>Fri, 13 Mar 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/03/13/wasm-game/</link>
          <guid>https://artificialworlds.net/blog/2026/03/13/wasm-game/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/03/13/wasm-game/">&lt;p&gt;Last time we did the hard work, so this time we can focus directly on writing a
fun game of Snake using pure Rust, running in a browser canvas via WASM.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;snake-rust-wasm&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;eXfichLZwz4uDxvDgwhspM&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Talking to localhost from inside an Android emulator</title>
          <pubDate>Tue, 03 Mar 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/03/03/android-emulator-talking-to-localhost/</link>
          <guid>https://artificialworlds.net/blog/2026/03/03/android-emulator-talking-to-localhost/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/03/03/android-emulator-talking-to-localhost/">&lt;p&gt;If you&#x27;re developing an Android application that talks to a server, you may well
want to run the server locally and talk to it from your Android emulator. If you
point at &lt;code&gt;localhost&lt;&#x2F;code&gt;, this means the Android device itself, not the server
running on your computer.&lt;&#x2F;p&gt;
&lt;p&gt;There is various information about how to make this work online, but the only
reliable way I have found is to run a command like this on the command line:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;adb reverse tcp:8000 tcp:8000
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(With thanks to this
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;62230663&quot;&gt;StackOverflow answer&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;This command makes &lt;code&gt;http:&#x2F;&#x2F;localhost:8000&lt;&#x2F;code&gt; on the Android device point at
&lt;code&gt;http:&#x2F;&#x2F;localhost:8000&lt;&#x2F;code&gt; on your computer, so if you are running a server on port
8000 on your computer, if you run the above command, you can talk to the server
from the Android emulator using this URL.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust: compiling to WASM to make a browser-based game using canvas</title>
          <pubDate>Fri, 27 Feb 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/02/27/wasm-game/</link>
          <guid>https://artificialworlds.net/blog/2026/02/27/wasm-game/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/02/27/wasm-game/">&lt;p&gt;I love writing Rust, and I love writing simple games, so let&#x27;s combine the two
by showing how to make a tiny game framework with these properties:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;99% of the code is Rust, compiled to WASM&lt;&#x2F;li&gt;
&lt;li&gt;the game runs in any non-ancient Web browser&lt;&#x2F;li&gt;
&lt;li&gt;the screen drawing is on a zoomed-in canvas so we can make pixelated
retro-games&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;set-up&quot;&gt;Set up&lt;&#x2F;h2&gt;
&lt;p&gt;Before we start, we need to install:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.org&#x2F;tools&#x2F;install&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt;, and&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;drager.github.io&#x2F;wasm-pack&#x2F;installer&#x2F;&quot;&gt;wasm-pack&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note: there is some confusion around documentation for WASM tools for Rust, with
the newest tools sometimes linking to outdated documentation. For the purposes
of this post, you can just install wasm-pack via the link above.&lt;&#x2F;p&gt;
&lt;p&gt;Want to watch me do this live? There&#x27;s a video...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;follow-along&quot;&gt;Follow along&lt;&#x2F;h2&gt;
&lt;p&gt;You can watch me create this setup live here:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust: compiling to WASM to make a browser-based game using canvas&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;pP4vxH7nuAseqMetMe7eVc&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;and you can find the full source code at:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;wasm-game&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;wasm-game&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon:
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we need to do is create a Rust+WASM project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-the-project&quot;&gt;Creating the project&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;code&gt;wasm-pack&lt;&#x2F;code&gt; provides us with a simple tool to create and build Rust code into
WASM. It&#x27;s a wrapper around standard &lt;code&gt;cargo&lt;&#x2F;code&gt; commands that sets things up the
way we need.&lt;&#x2F;p&gt;
&lt;p&gt;To create the project, type:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wasm-pack new wasm-game
cd wasm-game
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a project with the right defaults set up:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$ tree
.
├── Cargo.toml
├── src
│   ├── lib.rs
│   └── utils.rs
└── tests
    └── web.rs
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Have a look at &lt;code&gt;Cargo.toml&lt;&#x2F;code&gt;. It has the normal project info but it also adds a
dependency on &lt;code&gt;wasm-bindgen&lt;&#x2F;code&gt; which allows us to call Rust functions from
JavaScript, and JavaScript functions from Rust. It gives us
&lt;code&gt;console_error_panic_hook&lt;&#x2F;code&gt; which allows us to print panic messages from Rust on
the browser console, and it tells the compiler to optimise our WASM for size,
which is usually the right choice.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s look at &lt;code&gt;src&#x2F;lib.rs&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
mod utils;

use wasm_bindgen::prelude::*;

&lt;span style=&quot;color: green;&quot;&gt;#[wasm_bindgen]&lt;&#x2F;span&gt;
extern &quot;C&quot; {
    fn alert(s: &amp;str);
}

&lt;span style=&quot;color: green;&quot;&gt;#[wasm_bindgen]&lt;&#x2F;span&gt;
pub fn greet() {
    alert(&quot;Hello, wasm-game!&quot;);
}
&lt;&#x2F;pre&gt;
&lt;p&gt;The two highlighted lines containing &lt;code&gt;#[wasm_bindgen]&lt;&#x2F;code&gt; do two different things:
the first one tells the compiler that an external function exists: a JavaScript
function, despite the &lt;code&gt;extern &quot;C&quot;&lt;&#x2F;code&gt; part, which we want to call from within our
Rust code. By adding this, we make it possible to call &lt;code&gt;alert&lt;&#x2F;code&gt; from Rust, as we
do inside the &lt;code&gt;greet&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;The second &lt;code&gt;#[wasm_bindgen]&lt;&#x2F;code&gt; line does the opposite: it says we want the &lt;code&gt;greet&lt;&#x2F;code&gt;
function, which is written in Rust, to be callable from JavaScript code.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have some code, let&#x27;s compile it to WASM so we can run it in the
browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compiling-to-wasm&quot;&gt;Compiling to WASM&lt;&#x2F;h2&gt;
&lt;p&gt;Now we can compile all this Rust into WASM like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wasm-pack build --target=web
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This creates some files inside the &lt;code&gt;pkg&lt;&#x2F;code&gt; directory:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$ ls -1 pkg
package.json
wasm_game_bg.wasm
wasm_game_bg.wasm.d.ts
wasm_game.d.ts
wasm_game.js
...
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;package.json&lt;&#x2F;code&gt; is not relevant to us: it will help if we want to make an npm
package, but we don&#x27;t need that as we are writing code for the browser.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;.ts&lt;&#x2F;code&gt; files are type information that people can use if they want to call
our code from TypeScript. We&#x27;re not doing that so we&#x27;re not too interested.&lt;&#x2F;p&gt;
&lt;p&gt;What we are interested in is &lt;code&gt;wasm_game_bg.wasm&lt;&#x2F;code&gt;, which is the result of
compiling our Rust code into WASM, and &lt;code&gt;wasm_game.js&lt;&#x2F;code&gt;, which provides a simple
module we can import from JavaScript to run our code. If you open &lt;code&gt;wasm_game.js&lt;&#x2F;code&gt;
there is loads of stuff in there, but the important part is that there is an
exported function called &lt;code&gt;greet&lt;&#x2F;code&gt;, which is generated because we wrote a Rust
function called &lt;code&gt;greet&lt;&#x2F;code&gt; and annotated it &lt;code&gt;#[wasm_bindgen]&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So we&#x27;ve got some WASM and some JavaScript, but how do we actually run it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;running-in-the-browser&quot;&gt;Running in the browser&lt;&#x2F;h2&gt;
&lt;p&gt;We need to write some HTML. Let&#x27;s make a directory called &lt;code&gt;www&lt;&#x2F;code&gt; for all our
hand-written HTML, CSS and JavaScript, and create a file inside called
&lt;code&gt;index.html&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot; &#x2F;&amp;gt;
    &amp;lt;&#x2F;head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;script type=&quot;module&quot;&amp;gt;
            import &lt;span style=&quot;color: red;&quot;&gt;init&lt;&#x2F;span&gt;, { &lt;span style=&quot;color: green;&quot;&gt;greet&lt;&#x2F;span&gt; } from &quot;.&#x2F;wasm_game.js&quot;;
            await &lt;span style=&quot;color: red;&quot;&gt;init()&lt;&#x2F;span&gt;;
            &lt;span style=&quot;color: green;&quot;&gt;greet()&lt;&#x2F;span&gt;;
        &amp;lt;&#x2F;script&amp;gt;
    &amp;lt;&#x2F;body&amp;gt;
&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This HTML file is mostly actually JavaScript inside a &lt;code&gt;script&lt;&#x2F;code&gt; tag, and what
this code does is import the &lt;code&gt;greet&lt;&#x2F;code&gt; function from &lt;code&gt;wasm_game.js&lt;&#x2F;code&gt;, along with
&lt;code&gt;init&lt;&#x2F;code&gt;, which we always have to call at the beginning of our program, and then
it calls &lt;code&gt;greet&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We want our HTML and our generated WASM and accompanying files in the same
directory, so let&#x27;s copy them into one called &lt;code&gt;public&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p public
cp www&#x2F;* pkg&#x2F;*.js pkg&#x2F;*.ts pkg&#x2F;*.wasm public&#x2F;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we need a web server that serves up all the files inside &lt;code&gt;public&lt;&#x2F;code&gt;. I usually
use the one that comes with Python but you can use any one you like:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;cd public
python3 -m http.server
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This starts a web server, listening on port 8000, that serves up the files in
the &lt;code&gt;public&lt;&#x2F;code&gt; directory, which include &lt;code&gt;index.html&lt;&#x2F;code&gt;, &lt;code&gt;wasm_game.js&lt;&#x2F;code&gt; and our
compiled WASM code in &lt;code&gt;wasm_game_bg.wasm&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So if we open a web browser and go to &lt;code&gt;http:&#x2F;&#x2F;0.0.0.0:8000&#x2F;&lt;&#x2F;code&gt; we see an alert pop
up that says &quot;Hello, wasm-game&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Our Rust code has been compiled to WASM and is running in the browser! Have a
cup of tea.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-a-canvas&quot;&gt;Creating a canvas&lt;&#x2F;h2&gt;
&lt;p&gt;Now, to make an actual game, we&#x27;re going to need to be able to draw graphics. A
nice way to do that, especially for pixelated retro-games, is to use a canvas.
Let&#x27;s add one to &lt;code&gt;index.html&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset=&quot;utf-8&quot; &#x2F;&amp;gt;
        &lt;span style=&quot;color: green;&quot;&gt;&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; &#x2F;&amp;gt;&lt;&#x2F;span&gt;
    &amp;lt;&#x2F;head&amp;gt;
    &amp;lt;body&amp;gt;
        &lt;span style=&quot;color: green;&quot;&gt;&amp;lt;canvas id=&quot;canvas&quot; width=&quot;22&quot; height=&quot;22&quot;&amp;gt;&amp;lt;&#x2F;canvas&amp;gt;&lt;&#x2F;span&gt;

        &amp;lt;script type=&quot;module&quot;&amp;gt;
            import init, { greet } from &quot;.&#x2F;wasm_game.js&quot;;
            await init();
            greet();
        &amp;lt;&#x2F;script&amp;gt;
    &amp;lt;&#x2F;body&amp;gt;
&amp;lt;&#x2F;html&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ve added a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;&#x2F;code&gt; tag, with an id of &lt;code&gt;canvas&lt;&#x2F;code&gt;, and a width and height in
pixels - even when we stretch the canvas to fill the screen using CSS, it&#x27;s only
going to display 22x22 large scaled-up pixels, which will suit us very well.
Speaking of CSS, we&#x27;d better write just enough to centre and scale that canvas
to cover most of the screen. We create &lt;code&gt;style.css&lt;&#x2F;code&gt; inside &lt;code&gt;www&lt;&#x2F;code&gt; like this:&lt;&#x2F;p&gt;
&lt;pre&gt;
* {
    padding: 0px;
    margin: 0px;
}

body {
    &lt;span style=&quot;color: green;&quot;&gt;display: flex;&lt;&#x2F;span&gt;
    justify-content: center;
    align-items: center;
    height: 100vh;
}

#canvas {
    &lt;span style=&quot;color: green;&quot;&gt;width: 90vmin;&lt;&#x2F;span&gt;
    &lt;span style=&quot;color: red;&quot;&gt;image-rendering: pixelated;&lt;&#x2F;span&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;This makes the page body display using &lt;code&gt;flex&lt;&#x2F;code&gt; layout, which means we can centre
the canvas inside it, and the &lt;code&gt;width: 90vmin&lt;&#x2F;code&gt; instructs the canvas to be 90% of
the size of the shortest dimension of the screen. The &lt;code&gt;image-rendering&lt;&#x2F;code&gt; part
tells the canvas not to blur any of its huge pixels to make graphics look
smoother, but to draw them pixelated.&lt;&#x2F;p&gt;
&lt;p&gt;If your web server is still running, copy all the files into &lt;code&gt;public&lt;&#x2F;code&gt; again and
refresh the browser page. The screen will still be blank, but if you use the
development tools (press F12) you should be able to see the canvas within the
page using the Inspector tab.&lt;&#x2F;p&gt;
&lt;p&gt;I guess we&#x27;d better draw something on the canvas!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drawing-onto-the-canvas&quot;&gt;Drawing onto the canvas&lt;&#x2F;h2&gt;
&lt;p&gt;First, let&#x27;s rename &lt;code&gt;greet&lt;&#x2F;code&gt; in our Rust code to &lt;code&gt;start&lt;&#x2F;code&gt;. We&#x27;ll also need to
update the places in &lt;code&gt;index.html&lt;&#x2F;code&gt; that refer to &lt;code&gt;greet&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;In &lt;code&gt;src&#x2F;lib.rs&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
...

#[wasm_bindgen]
pub fn &lt;span style=&quot;color: green;&quot;&gt;start&lt;&#x2F;span&gt;() {
    set_panic_hook();

    alert(&quot;Hello, wasm-game!&quot;);
}
&lt;&#x2F;pre&gt;
&lt;p&gt;In &lt;code&gt;www&#x2F;index.html&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
...
        &amp;lt;script type=&quot;module&quot;&amp;gt;
            import init, { &lt;span style=&quot;color: green;&quot;&gt;start&lt;&#x2F;span&gt; } from &quot;.&#x2F;wasm_game.js&quot;;
            await init();
            &lt;span style=&quot;color: green;&quot;&gt;start&lt;&#x2F;span&gt;();
        &amp;lt;&#x2F;script&amp;gt;
...
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that we also added a call to &lt;code&gt;set_panic_hook&lt;&#x2F;code&gt; at the top of &lt;code&gt;start&lt;&#x2F;code&gt; -
we&#x27;ll need to import that from our &lt;code&gt;utils&lt;&#x2F;code&gt; module that was created by &lt;code&gt;wasm-pack new&lt;&#x2F;code&gt;. Setting a panic hook here means that if our Rust code panics, the panic
message will be printed to the browser console, which is very handy.&lt;&#x2F;p&gt;
&lt;p&gt;Build the code and copy everything into &lt;code&gt;public&lt;&#x2F;code&gt; again:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;wasm-pack build --target=web
cp www&#x2F;* pkg&#x2F;*.js pkg&#x2F;*.ts pkg&#x2F;*.wasm public&#x2F;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure the web server is still running, and refresh the browser. It should
still nag you with the same &quot;Hello&quot; message.&lt;&#x2F;p&gt;
&lt;p&gt;Now we need to write some non-trivial code inside that &lt;code&gt;start&lt;&#x2F;code&gt; function.&lt;&#x2F;p&gt;
&lt;p&gt;First, we need to ask for the ability to call a load of browser-native functions
from Rust. To do that, we&#x27;ll add the &lt;code&gt;web-sys&lt;&#x2F;code&gt; crate as a dependency.&lt;&#x2F;p&gt;
&lt;p&gt;In Cargo.toml, just above &lt;code&gt;[dev-dependencies]&lt;&#x2F;code&gt; let&#x27;s add this section:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;[dependencies.web-sys]
features = [
    &amp;quot;CanvasRenderingContext2d&amp;quot;,
    &amp;quot;Document&amp;quot;,
    &amp;quot;Element&amp;quot;,
    &amp;quot;Event&amp;quot;,
    &amp;quot;HtmlCanvasElement&amp;quot;,
    &amp;quot;KeyboardEvent&amp;quot;,
    &amp;quot;Window&amp;quot;,
    &amp;quot;console&amp;quot;,
]
version = &amp;quot;0.3.88&amp;quot;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If this looks weird, it&#x27;s equivalent to adding a single line inside the
&lt;code&gt;[dependencies]&lt;&#x2F;code&gt; section that looks like
&lt;code&gt;web-sys = { version = &quot;0.3.88&quot;, features = [...&lt;&#x2F;code&gt; but it wraps much better onto
multiple lines. (Re-arranging it this way is a feature of the TOML configuration
language rather than &lt;code&gt;cargo&lt;&#x2F;code&gt; specifically.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;web-sys&lt;&#x2F;code&gt; provides browser functions that we can call from Rust, but it
separates everything into features, so if you want to use a particular browser
feature, e.g. the &lt;code&gt;Window&lt;&#x2F;code&gt; object, you need to specify it in &lt;code&gt;features&lt;&#x2F;code&gt;.
Most of the features, like &lt;code&gt;Window&lt;&#x2F;code&gt;, are very directly named after the object
that you want to use. We&#x27;ve added all the features we will want immediately, to
avoid messing around later.&lt;&#x2F;p&gt;
&lt;p&gt;I promised you some Rust code, so let&#x27;s update our &lt;code&gt;start&lt;&#x2F;code&gt; function to draw
something on the canvas:&lt;&#x2F;p&gt;
&lt;pre&gt;
#[wasm_bindgen]
pub fn start() {
    set_panic_hook();

    let window = window().unwrap();
    let document = window.document().unwrap();

    let canvas: HtmlCanvasElement = document
        .get_element_by_id(&quot;canvas&quot;)
        .unwrap()
        .dyn_into()
        .unwrap();

    let context: CanvasRenderingContext2d = canvas
        &lt;span style=&quot;color: green;&quot;&gt;.get_context(&quot;2d&quot;)&lt;&#x2F;span&gt;
        .unwrap()
        .unwrap()
        .dyn_into()
        .unwrap();

    context.&lt;span style=&quot;color: red;&quot;&gt;fill_rect&lt;&#x2F;span&gt;(3.0, 2.0, 1.0, 1.0);
    context.&lt;span style=&quot;color: red;&quot;&gt;fill_rect&lt;&#x2F;span&gt;(4.0, 3.0, 1.0, 1.0);
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Note that you&#x27;ll need to add some imports at the top for this to compile - most
of them come from the &lt;code&gt;web-sys&lt;&#x2F;code&gt; crate we just added.&lt;&#x2F;p&gt;
&lt;p&gt;This code grabs a reference to the browser window, then uses that to get a
reference to the document being displayed. The document has a
&lt;code&gt;get_element_by_id&lt;&#x2F;code&gt; method that we can use to find our canvas. If you&#x27;re
familiar with writing JavaScript in a browser, you will be used to spelling this
&lt;code&gt;getElementById&lt;&#x2F;code&gt;. All the names are translated in this way into things that feel
more familiar to Rust programmers.&lt;&#x2F;p&gt;
&lt;p&gt;Once we have our canvas, to actually draw on it we need a context (of type
&quot;2d&quot;). Once we have a context, we can actually do some drawing operations.&lt;&#x2F;p&gt;
&lt;p&gt;If you type in this code and do the usual compile-and-copy routine, then refresh
the browser, you should see some black squares being drawn on your canvas!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve done almost enough to create a game: the most complicated parts are still
to come, but at least they are mostly Rust code!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;listening-for-key-presses&quot;&gt;Listening for key presses&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s listen for the user pressing arrow keys, and store the last direction they
pressed in a &lt;code&gt;Game&lt;&#x2F;code&gt; struct.&lt;&#x2F;p&gt;
&lt;p&gt;First we&#x27;ll create &lt;code&gt;Game&lt;&#x2F;code&gt;, and a supporting enum called &lt;code&gt;Dir&lt;&#x2F;code&gt;. You can put all
this inside &lt;code&gt;lib.rs&lt;&#x2F;code&gt; or use separate modules if you prefer:&lt;&#x2F;p&gt;
&lt;pre&gt;
#[derive(Debug)]
pub enum Dir {
    Left,
    Up,
    Right,
    Down,
}

pub struct Game {
    dir: Dir,
}

impl Game {
    fn new() -&gt; Self {
        Self { dir: Dir::Left }
    }

    fn &lt;span style=&quot;color: green;&quot;&gt;on_keydown&lt;&#x2F;span&gt;(&amp;mut self, event: KeyboardEvent) {
        match event.key_code() {
            &lt;span style=&quot;color: green;&quot;&gt;37&lt;&#x2F;span&gt; =&gt; self.dir = &lt;span style=&quot;color: red;&quot;&gt;Dir::Left&lt;&#x2F;span&gt;,
            38 =&gt; self.dir = Dir::Up,
            39 =&gt; self.dir = Dir::Right,
            40 =&gt; self.dir = Dir::Down,
            _ =&gt; {}
        }
        console::log_1(&amp;format!(&quot;{:?}&quot;, self.dir).into());
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;The interesting part here is the &lt;code&gt;on_keydown&lt;&#x2F;code&gt; method, which expects to receive a
&lt;code&gt;KeyboardEvent&lt;&#x2F;code&gt; (another browser type provided by &lt;code&gt;web-sys&lt;&#x2F;code&gt;) and updates our
&lt;code&gt;Game&lt;&#x2F;code&gt; to store the correct direction based on the key the user pressed. The key
codes we&#x27;ve used are for the arrow keys.&lt;&#x2F;p&gt;
&lt;p&gt;Now that we have a &lt;code&gt;Game&lt;&#x2F;code&gt; that is ready to receive keyboard events, let&#x27;s delete
all that canvas code from &lt;code&gt;start&lt;&#x2F;code&gt; and add an event listener to provide them (we&#x27;ll
re-add the canvas code soon, elsewhere):&lt;&#x2F;p&gt;
&lt;pre&gt;
#[wasm_bindgen]
pub fn start() {
    set_panic_hook();

    let game = Arc::new(Mutex::new(&lt;span style=&quot;color: green;&quot;&gt;Game::new()&lt;&#x2F;span&gt;));

    let window = window().unwrap();

    let game_clone = Arc::clone(&amp;game);
    let on_keydown: Closure&lt;dyn Fn(Event)&gt; = Closure::new(
        &lt;span style=&quot;color: red;&quot;&gt;move |event: Event| {
            game_clone
                .lock()
                .unwrap()
                .on_keydown(event.dyn_into().unwrap())
        }&lt;&#x2F;span&gt;
    );

    window
        .&lt;span style=&quot;color: green;&quot;&gt;add_event_listener_with_callback&lt;&#x2F;span&gt;(
            &quot;keydown&quot;,
            &lt;span style=&quot;color: red;&quot;&gt;on_keydown.as_ref().unchecked_ref()&lt;&#x2F;span&gt;
        ).unwrap();

    on_keydown.forget();
}
&lt;&#x2F;pre&gt;
&lt;p&gt;First we great a new &lt;code&gt;Game&lt;&#x2F;code&gt; instance and wrap it in an &lt;code&gt;Arc&lt;&#x2F;code&gt; of a &lt;code&gt;Mutex&lt;&#x2F;code&gt;
because it&#x27;s going to be used by lots of potentially-concurrent code such as
event handlers, so we need to be able to call &lt;code&gt;lock&lt;&#x2F;code&gt; to wait our turn to get
access to it.&lt;&#x2F;p&gt;
&lt;p&gt;Then we create a &lt;code&gt;Closure&lt;&#x2F;code&gt;, which is a way of wrapping Rust closures so they can
be called by JavaScript code. The closure that we wrap calls &lt;code&gt;Game&lt;&#x2F;code&gt;&#x27;s
&lt;code&gt;on_keydown&lt;&#x2F;code&gt; method and passes through the event it receives. Once we&#x27;ve made
our &lt;code&gt;Closure&lt;&#x2F;code&gt;, we can convert it into a &lt;code&gt;Function&lt;&#x2F;code&gt; type (by calling
&lt;code&gt;.as_ref().unchecked_ref()&lt;&#x2F;code&gt;), which is what we need to pass in to
&lt;code&gt;add_event_listener_with_callback&lt;&#x2F;code&gt;. We are asking that every time a &lt;code&gt;keydown&lt;&#x2F;code&gt;
event happens, our closure should be called.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we call &lt;code&gt;forget&lt;&#x2F;code&gt; on the Closure object, because otherwise it will get
dropped by Rust when we leave this function, so it won&#x27;t exist any more when it
is called as a callback.&lt;&#x2F;p&gt;
&lt;p&gt;If you compile-and-copy and refresh the browser, then click in the main window
and press some arrow keys, you should see some debug messages printed to the
Console tab of the developer tools, saying which direction you are facing!&lt;&#x2F;p&gt;
&lt;p&gt;Now, if we&#x27;re going to make a game, we will need a game loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-game-loop-with-requestanimationframe&quot;&gt;A game loop with requestAnimationFrame&lt;&#x2F;h2&gt;
&lt;p&gt;The nicest way to make a game loop in the browser is with a built-in function
called &lt;code&gt;requestAnimationFrame&lt;&#x2F;code&gt;, which tries to call your code about 60 times per
second. To use it, you have to re-call &lt;code&gt;requestAnimationFrame&lt;&#x2F;code&gt; from inside the
callback that was called by it last frame. Doing that inside a Rust closure is
slightly tricky, but here&#x27;s a recipe you can paste into the &lt;code&gt;start&lt;&#x2F;code&gt; function
after what is already there:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;    let on_frame_refcell: Rc&amp;lt;RefCell&amp;lt;Option&amp;lt;Closure&amp;lt;dyn Fn(BigInt)&amp;gt;&amp;gt;&amp;gt;&amp;gt; =
        Rc::new(RefCell::new(None));

    let window_clone = window.clone();
    let on_frame_refcell_clone = Rc::clone(&amp;amp;on_frame_refcell);
    let callback = move |ts: BigInt| {
        game.lock().unwrap().on_frame(ts.unchecked_into_f64());

        window_clone
            .request_animation_frame(
                on_frame_refcell_clone
                    .borrow()
                    .as_ref()
                    .unwrap()
                    .as_ref()
                    .unchecked_ref(),
            )
            .unwrap();
    };
    let cb: Closure&amp;lt;dyn Fn(BigInt)&amp;gt; = Closure::new(callback);

    on_frame_refcell.replace(Some(cb));

    window
        .request_animation_frame(
            on_frame_refcell
                .borrow()
                .as_ref()
                .unwrap()
                .as_ref()
                .unchecked_ref(),
        )
        .unwrap();
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you want the detail on how all this works, I suggest watching the video
at the top of this page. For now, suffice it to say that we have to hold the
closure in a &lt;code&gt;Rc&amp;lt;RefCell&amp;lt;_&amp;gt;&amp;gt;&lt;&#x2F;code&gt; so that we can refer to it from inside itself.&lt;&#x2F;p&gt;
&lt;p&gt;For this to work we&#x27;ll need to add quite a few imports, and also add one more
method to &lt;code&gt;Game&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
    pub fn on_frame(&amp;mut self, ts: f64) {
        console::log_1(&amp;&quot;on_frame called&quot;.into());
    }
&lt;&#x2F;pre&gt;
&lt;p&gt;If we do the usual compile-and-copy routine and refresh the browser, we should
see the &quot;on_frame called&quot; message appearing in the Console section with a number
next to it that shoots up as it is called 60 times per second.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve almost got enough to make a game. Let&#x27;s quickly add back some drawing to
the canvas, and reduce the frame rate a bit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drawing-every-frame-and-a-lower-frame-rate&quot;&gt;Drawing every frame, and a lower frame rate&lt;&#x2F;h2&gt;
&lt;p&gt;Inside &lt;code&gt;Game&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;on_frame&lt;&#x2F;code&gt; method, we are going to throttle back the frame rate,
only doing anything every 400ms or so, and we&#x27;re going to draw some stuff on the
canvas. Hopefully this will demonstrate how a real game can work: inside the new
&lt;code&gt;step&lt;&#x2F;code&gt; method, we expect we would run some game logic (that will probably
update some properties that are held within &lt;code&gt;Game&lt;&#x2F;code&gt;), and then draw the outcome
onto the canvas.&lt;&#x2F;p&gt;
&lt;p&gt;First, we need to supply the canvas to the &lt;code&gt;Game&lt;&#x2F;code&gt;, so inside &lt;code&gt;start&lt;&#x2F;code&gt;, let&#x27;s get
hold of the canvas and pass it in to &lt;code&gt;new&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
#[wasm_bindgen]
pub fn start() {
    &#x2F;&#x2F; ...

    let canvas: HtmlCanvasElement = document
        .&lt;span style=&quot;color: green;&quot;&gt;get_element_by_id&lt;&#x2F;span&gt;(&quot;canvas&quot;)
        .unwrap()
        .dyn_into()
        .unwrap();

    let game = Arc::new(Mutex::new(Game::new(&lt;span style=&quot;color: red;&quot;&gt;canvas&lt;&#x2F;span&gt;)));

    &#x2F;&#x2F; ...
&lt;&#x2F;pre&gt;
&lt;p&gt;and let&#x27;s adapt &lt;code&gt;Game&lt;&#x2F;code&gt; to hold onto it:&lt;&#x2F;p&gt;
&lt;pre&gt;
pub struct Game {
    &lt;span style=&quot;color: green;&quot;&gt;canvas&lt;&#x2F;span&gt;: HtmlCanvasElement,
    dir: Dir,
    &lt;span style=&quot;color: red;&quot;&gt;next_frame_ts&lt;&#x2F;span&gt;: f64,
}

impl Game {
    pub fn new(&lt;span style=&quot;color: green;&quot;&gt;canvas&lt;&#x2F;span&gt;: HtmlCanvasElement) -&gt; Self {
        Self {
            &lt;span style=&quot;color: green;&quot;&gt;canvas&lt;&#x2F;span&gt;,
            dir: Dir::Up,
            &lt;span style=&quot;color: red;&quot;&gt;next_frame_ts&lt;&#x2F;span&gt;: 0.0,
        }
    }
    &#x2F;&#x2F; ...
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that while we were there, along with &lt;code&gt;canvas&lt;&#x2F;code&gt;, we also added
&lt;code&gt;next_frame_ts&lt;&#x2F;code&gt;. We are going to use that to detect when 400ms have passed, so
we should process another frame. This effectively slows the frame rate from 60
FPS to about 2.5 FPS:&lt;&#x2F;p&gt;
&lt;pre&gt;
    pub fn on_frame(&amp;mut self, &lt;span style=&quot;color: green;&quot;&gt;ts&lt;&#x2F;span&gt;: f64) {
        if &lt;span style=&quot;color: green;&quot;&gt;ts&lt;&#x2F;span&gt; &gt; self.&lt;span style=&quot;color: red;&quot;&gt;next_frame_ts&lt;&#x2F;span&gt; {
            self.step();
            self.&lt;span style=&quot;color: red;&quot;&gt;next_frame_ts&lt;&#x2F;span&gt; = &lt;span style=&quot;color: green;&quot;&gt;ts&lt;&#x2F;span&gt; + 400.0;
        }
    }
&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;ts&lt;&#x2F;code&gt; argument increases as time goes on, so we wait until it&#x27;s time for the
next frame, and call &lt;code&gt;step&lt;&#x2F;code&gt; when it is. We&#x27;d better add a &lt;code&gt;step&lt;&#x2F;code&gt; method:&lt;&#x2F;p&gt;
&lt;pre&gt;
    fn step(&amp;mut self) {
        console::log_1(&amp;format!(&quot;Inside step dir={:?}&quot;, self.dir).into());

        let context: CanvasRenderingContext2d = self
            .canvas
            .&lt;span style=&quot;color: green;&quot;&gt;get_context&lt;&#x2F;span&gt;(&quot;2d&quot;)
            .unwrap()
            .unwrap()
            .dyn_into()
            .unwrap();

        context.&lt;span style=&quot;color: red;&quot;&gt;fill_rect&lt;&#x2F;span&gt;(3.0, 2.0, 1.0, 1.0);
        context.&lt;span style=&quot;color: red;&quot;&gt;fill_rect&lt;&#x2F;span&gt;(4.0, 3.0, 1.0, 1.0);
    }
&lt;&#x2F;pre&gt;
&lt;p&gt;If we compile-and-copy and refresh the browser, we can see that this prints a
log entry in the Console tab a couple of times a second, and draws on the canvas
as well.&lt;&#x2F;p&gt;
&lt;p&gt;This should be enough to get you going writing a little game in Rust that runs
inside your browser canvas. Watch my later videos to see what games I come up
with!&lt;&#x2F;p&gt;
&lt;p&gt;Can you make it draw something different every time &lt;code&gt;step&lt;&#x2F;code&gt; is called?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Billion row challenge: parsing temperatures fast(ish)</title>
          <pubDate>Fri, 20 Feb 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/02/20/billion-parse-temp/</link>
          <guid>https://artificialworlds.net/blog/2026/02/20/billion-parse-temp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/02/20/billion-parse-temp/">&lt;p&gt;I watched &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;tCY7p6dVAGE?si=YCZlw_clQnjAyb2n&quot;&gt;Jon Gjengset taking on the one billion row challenge&lt;&#x2F;a&gt;
and got interested in one small corner of it: parsing temperatures, and in
particular parsing temperatures in a &quot;branchless&quot; style - with no &lt;code&gt;if&lt;&#x2F;code&gt;
statements - to help the CPU run the code as fast as possible. In the end, the
results were surprising.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Billion row challenge: parsing temperatures fast(ish)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;bBkw2mCziP2Eax3nCC2mqf&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;After the stream I added a version of Jon&#x27;s &lt;code&gt;parse_temperature&lt;&#x2F;code&gt; to the
comparison. His version assumes we already know where the semicolon is, so I had
to add code to find it, and I didn&#x27;t use the SIMD code he used to do that
because it requires unstable Rust. With those caveats, my version was 4x
faster(!). However, I&#x27;m pretty sure we&#x27;re going to need to find that semicolon
ourselves if we take on the full challenge, so this is not a fair comparison.&lt;&#x2F;p&gt;
&lt;p&gt;Also after the stream I realised I&#x27;m pretty sure there is a simple improvement I
can make to branchy to make it even faster...&lt;&#x2F;p&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust: building arbitrary precision integers (badly)</title>
          <pubDate>Fri, 06 Feb 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/02/06/arbitrary-precision-integers/</link>
          <guid>https://artificialworlds.net/blog/2026/02/06/arbitrary-precision-integers/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/02/06/arbitrary-precision-integers/">&lt;p&gt;I thought it might be quite straightforward to show the basics of how we could
design a &quot;BigInt&quot; type of struct. It turns out it was more involved than I
thought it would be, but we got a little way into it. Maybe we can continue in a
later video?&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust: building arbitrary precision integers (badly)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1J69YnmCAqQUkj65Y4Rpnk&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust: how to return a closure from a function, and what are closures anyway?</title>
          <pubDate>Fri, 30 Jan 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/01/30/returning-closures/</link>
          <guid>https://artificialworlds.net/blog/2026/01/30/returning-closures/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/01/30/returning-closures/">&lt;p&gt;Sometimes we want to return a closure from a function, and sometimes that
doesn&#x27;t work the way we might expect. This will lead us on to how we should
think of closures: they&#x27;re not really like functions so much as
automatically-generated structs with types we are not allowed to name. And all
closures have different types, even if they&#x27;re identical...?&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust: how to return a closure from a function, and what are closures anyway?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;sQnHfXhASaWKiHegokruXr&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FizzBuzz 2: FizzBuzz Harder</title>
          <pubDate>Fri, 23 Jan 2026 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2026/01/23/fizzbuzz2/</link>
          <guid>https://artificialworlds.net/blog/2026/01/23/fizzbuzz2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2026/01/23/fizzbuzz2/">&lt;p&gt;I implemented FizzBuzz before, but in retrospect I don&#x27;t like what I did. I
especially don&#x27;t like my tests. So let&#x27;s try to do better, and talk about why
it&#x27;s better.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;FizzBuzz 2: FizzBuzz Harder&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2dEHo81R7ozrfohX2PARWt&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I don&#x27;t use AI</title>
          <pubDate>Tue, 16 Dec 2025 10:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/12/16/why-i-dont-use-ai/</link>
          <guid>https://artificialworlds.net/blog/2025/12/16/why-i-dont-use-ai/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/12/16/why-i-dont-use-ai/">&lt;p&gt;(Balaam, A.J. (2025). In F. Buontempo, editor,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;journals&#x2F;overload&#x2F;overload190&quot;&gt;Overload 190 (2025)&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;I choose to avoid using &quot;AI&quot; (by which I mean Large Language Models&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#10&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;). Here&#x27;s
why:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;they have a devastating environmental impact,&lt;&#x2F;li&gt;
&lt;li&gt;they are trained by exploiting and traumatising millions of low-paid workers,&lt;&#x2F;li&gt;
&lt;li&gt;they produce biased and dangerously incorrect results, and&lt;&#x2F;li&gt;
&lt;li&gt;they unfairly use people&#x27;s creative work.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;environmental-impact&quot;&gt;Environmental impact&lt;&#x2F;h2&gt;
&lt;p&gt;Between 2010 and 2020, the energy used by data centres around the world rose
only slightly&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#11&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, but since then energy use has risen sharply&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#14&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, driven by
the expansion of AI&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#15&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Compounding the problem, because these data centres
are using more energy than was predicted or provided for by existing generation,
the carbon intensity of the electricity use is much higher than average (48%
higher in the US according to one study&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#15&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Driven by AI, data centres are predicted to double their energy use by
2030&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#12&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. In Ireland in 2025, data centres use almost a fifth of the
electricity supply&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#13&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, despite the rise in use of electric vehicles over
recent years.&lt;&#x2F;p&gt;
&lt;p&gt;Unless we change course, this is not going to slow down or become sustainable.
Quoting Sam Altman: &quot;You should expect OpenAI to spend trillions of dollars on
data center construction in the not very distant future&quot; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#17&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This rapacious appetite for more data and more computation is hard-wired into
the AI movement. It is driven by a belief that Sam Altman expresses like this on
his blog: &quot;it appears that you can spend arbitrary amounts of money and get
continuous and predictable gains&quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#18&quot;&gt;8&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. The movement is built on the idea that
if we just consume more and more resources, we will achieve greater and greater
success. As long as they are driven by this belief, we can never expect them
even to attempt to curb their energy use.&lt;&#x2F;p&gt;
&lt;p&gt;Data centres are often harmful to the local area, and are often sited in areas
of existing social deprivation. They consume both energy and water that could
otherwise be used by people, and cause problems with pollution and energy
shortages&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;9&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For more detail on the environmental impacts of AI I recommend (perhaps
surprisingly) the Teen Vogue article &quot;ChatGPT Is Everywhere — Why Aren&#x27;t We
Talking About Its Environmental Costs?&quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;10&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;exploitation-of-workers&quot;&gt;Exploitation of workers&lt;&#x2F;h2&gt;
&lt;p&gt;The AI companies don&#x27;t like to talk about it, but their models only work when
provided with vast amounts of human-created data. This data is not simply
passively scraped from the Internet: the models are built on the work of
millions of people actively classifying images and rating answers, shaping the
models to produce results that look and sound safe and reasonable&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;11&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the people involved are very poorly paid&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;12&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Many of them are
traumatised by horrific images and speech that they are asked to classify&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#19&quot;&gt;13&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Workers paid between $1.32 and $2 per hour in Kenya (a wage described as &quot;an
insult&quot;) talk about their work like this: &quot;You’re reading this content, day in,
day out, over the course of days and weeks and months, it seeps into your brain
and you can’t get rid of it.&quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#33&quot;&gt;14&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;biased-and-dangerous-results&quot;&gt;Biased and dangerous results&lt;&#x2F;h2&gt;
&lt;p&gt;Despite wide acknowledgement among experts that AI produces unreliable results,
many people are being encouraged to trust its output in terms of accuracy and
safety.&lt;&#x2F;p&gt;
&lt;p&gt;Researchers have found that recent AI models confidently express judgements that
are plain wrong, making mistakes about basic economic ideas like interest
rates&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#26&quot;&gt;15&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, or inventing conversations about patients&#x27; medical data&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;16&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Even more concerningly, people are treating AI models as trustworthy
conversation partners. This is done with full encouragement from the AI
companies, despite the real risks involved. In 2023, Character AI founder Noam
Shazeer said of AI&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#25&quot;&gt;17&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &quot;It’s going to be super, super helpful to a lot of
people who are lonely or depressed.&quot; In fact, one of Character AI&#x27;s chat bots
played an alarming role in the suicide of a teenager&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#25&quot;&gt;17&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. The parents of
another teenager say it explicitly encouraged him to commit suicide before he
did&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#7&quot;&gt;18&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. There is a growing number of reports of chat bots guiding people down
&quot;delusional spirals&quot; that can have devastating mental health consequences&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#28&quot;&gt;19&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It is clear from all these examples and many more court cases currently in
progress that it is impossible to control the words spewing from these models.
Given the racial slurs included in the most widely-used training dataset&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#16&quot;&gt;20&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; it
is not surprising that they occasionally lose the plot like Grok once did,
producing racist rants and naming itself &quot;MechaHitler&quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#6&quot;&gt;21&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Throughout all of this unreliability, the popular AI models very reliably convey
total confidence in the latest answer they gave, even where it contradicts the
previous one.&lt;&#x2F;p&gt;
&lt;p&gt;It is pure wishful thinking to say that AI models can replace human judgement in
any area. If people treat AI as a trustworthy oracle or a trustworthy companion,
this wishful thinking is actively harmful&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#31&quot;&gt;22&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unfair-use-of-creative-work&quot;&gt;Unfair use of creative work&lt;&#x2F;h2&gt;
&lt;p&gt;The leading models are trained on all the data the AI companies can get their
hands on, regardless of license. This includes proprietary information such as
news and information sites, art galleries and personal web sites that are
protected by traditional copyright arrangements. These sites (and indeed
printed books and other offline materials) are published under a legal
framework that allows searching and indexing of their content without reproducing
it. Many commercial web sites depend on visitors being directed towards their
web site so they can receive advertising income.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, a huge amount of material is available online in free and open source
form, especially but not exclusively the enormous corpus of source code that is
used to train AI coding models. The bargain for this material is different:
authors require attribution for re-use, and place additional requirements such
as &quot;share-alike&quot; clauses that enforce the release of derived works under the
same terms. AI models are breaking this legally-enforced bargain by reproducing
derived or straightforwardly copied works with no attribution or correct
license.&lt;&#x2F;p&gt;
&lt;p&gt;Directing visitors to web sites is not a benevolent or coincidental side effect
of search engines: it is a self-sustaining bargain: you allow me to index your
content and in exchange I direct users to your site. If this bargain breaks
down, web site creators lose their source of income and many web sites will
disappear&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#32&quot;&gt;23&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Complying with license terms is not optional: it is required to use
any material, including free and open source content.&lt;&#x2F;p&gt;
&lt;p&gt;These bargains are enforced by copyright law. The invention of AI did not change
anything about this bargain, except that it obfuscated the copying of
copyrighted material &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#8&quot;&gt;24&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#9&quot;&gt;25&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and convinced governments that enforcing the law
would block the promised economic miracle of AI&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#29&quot;&gt;26&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#30&quot;&gt;27&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-reasons&quot;&gt;Other reasons&lt;&#x2F;h2&gt;
&lt;p&gt;This article is primarily concerned with ethical reasons for avoiding AI usage,
but there are plenty of other reasons too:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;They don&#x27;t do what the billionaires claim they do: researchers found that open
source developers were 19% less productive when using AI tools&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#20&quot;&gt;28&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and a UK
study showed no productivity gain&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#27&quot;&gt;29&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;They make you worse at your job: doctors rapidly experienced erosion of their
ability to spot cancer when they began using AI tools&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#21&quot;&gt;30&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;They will be used as an excuse to cut jobs: Amazon recently told its workers
to expect job cuts due to AI&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#22&quot;&gt;31&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; even though it has been engaging in regular
job cuts since 2022.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Despite the hype, it is clear that AI can perform some tasks effectively, for
example making very convincing fake videos. I choose to avoid them where I can,
for the above reasons.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;I believe that AI is a force that is doing real harm in our world, and is
concentrating wealth and power in the hands of those who are already wealthy and
powerful enough. If you agree, let&#x27;s work together as professionals to help our
companies, organisation and friends to be skeptical of its benefits, and mindful
of its problems when we make decisions about how and where to use it.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to hear more AI-skeptical viewpoints, thecon.ai&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#23&quot;&gt;32&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; is a good
place to start. The article &quot;I Am An AI Hater&quot;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#24&quot;&gt;33&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; by Anthony Moser was the
research starting point for this article and is recommended if you&#x27;d like a less
emotionally-constrained view along similar lines.&lt;&#x2F;p&gt;
&lt;p&gt;(With thanks to the Overload reviewers for suggesting several extra references.)&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;10&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.teenvogue.com&#x2F;story&#x2F;chatgpt-is-everywhere-environmental-costs-oped&quot;&gt;ChatGPT Is Everywhere - Why Aren&#x27;t We Talking About Its Environmental Costs? - Teen Vogue&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;9&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;articles&#x2F;cy8gy7lv448o&quot;&gt;&#x27;I can&#x27;t drink the water&#x27; - life next to a US data centre - BBC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;11&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.noemamag.com&#x2F;the-exploited-labor-behind-artificial-intelligence&#x2F;&quot;&gt;The Exploited Labor Behind Artificial Intelligence - Noema&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;4&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;12&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wired.com&#x2F;story&#x2F;millions-of-workers-are-training-ai-models-for-pennies&#x2F;&quot;&gt;Millions of Workers Are Training AI Models for Pennies - Wired&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;5&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;16&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;apnews.com&#x2F;article&#x2F;ai-artificial-intelligence-health-business-90020cdf5fa16c79ca2e5b6c4c9bbb14&quot;&gt;Researchers say an AI-powered transcription tool used in hospitals invents things no one ever said - AP News&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;6&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;21&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.npr.org&#x2F;2025&#x2F;07&#x2F;09&#x2F;nx-s1-5462609&#x2F;grok-elon-musk-antisemitic-racist-content&quot;&gt;Elon Musk&#x27;s AI chatbot, Grok, started calling itself &#x27;MechaHitler&#x27; - NPR&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;7&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;18&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;2025&#x2F;08&#x2F;26&#x2F;technology&#x2F;chatgpt-openai-suicide.html&quot;&gt;A Teen Was Suicidal. ChatGPT Was the Friend He Confided In - New York Times&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;8&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;24&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jskfellows.stanford.edu&#x2F;theft-is-not-fair-use-474e11f0d063&quot;&gt;Theft is not fair use - David Carson&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;9&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;25&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;technology-67826601&quot;&gt;New York Times sues Microsoft and OpenAI for &#x27;billions&#x27; - BBC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;10&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Large_language_model&quot;&gt;Large language model - Wikipedia&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;11&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wired.com&#x2F;story&#x2F;data-centers-not-devouring-planet-electricity-yet&#x2F;&quot;&gt;Data Centers Aren&#x27;t Devouring the Planet&#x27;s Electricity - Yet - Wired&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;12&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nature.com&#x2F;articles&#x2F;d41586-025-01113-z&quot;&gt;Data centres will use twice as much energy by 2030 - driven by AI - Nature&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;13&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;articles&#x2F;cpe9l5ke5jvo&quot;&gt;Data centres use almost a fifth of Irish electricity&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;14&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reuters.com&#x2F;business&#x2F;energy&#x2F;us-data-center-power-use-could-nearly-triple-by-2028-doe-backed-report-says-2024-12-20&#x2F;&quot;&gt;US data-center power use could nearly triple by 2028, DOE-backed report says - Reuters&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;15&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.technologyreview.com&#x2F;2025&#x2F;05&#x2F;20&#x2F;1116327&#x2F;ai-energy-usage-climate-footprint-big-tech&#x2F;&quot;&gt;We did the math on AI’s energy footprint. Here’s the story you haven’t heard - MIT Technology Review&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;16&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;20&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;undark.org&#x2F;2021&#x2F;10&#x2F;18&#x2F;computer-scientists-try-to-sidestep-ai-data-dilemma&#x2F;&quot;&gt;Limits to Growth: Can AI’s Voracious Appetite for Data Be Tamed? - Undark&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;17&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;fortune.com&#x2F;2025&#x2F;08&#x2F;18&#x2F;sam-altman-openai-chatgpt5-launch-data-centers-investments&#x2F;&quot;&gt;Sam Altman admits OpenAI ‘totally screwed up’ its GPT-5 launch and says the company will spend trillions of dollars on data centers - Fortune&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;18&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;8&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.samaltman.com&#x2F;three-observations&quot;&gt;Three Observations - Sam Altman&#x27;s blog&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;19&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;13&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cbsnews.com&#x2F;news&#x2F;labelers-training-ai-say-theyre-overworked-underpaid-and-exploited-60-minutes-transcript&#x2F;&quot;&gt;Labelers training AI say they&#x27;re overworked, underpaid and exploited by big American tech companies - CBS News&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;20&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;28&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;metr.org&#x2F;blog&#x2F;2025-07-10-early-2025-ai-experienced-os-dev-study&#x2F;&quot;&gt;Measuring the Impact of Early-2025 AI on Experienced Open-Source Developer Productivity - Joel Becker et al&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;21&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;30&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bloomberg.com&#x2F;news&#x2F;articles&#x2F;2025-08-12&#x2F;ai-eroded-doctors-ability-to-spot-cancer-within-months-in-study?embedded-checkout=true&quot;&gt;AI Eroded Doctors’ Ability to Spot Cancer Within Months in Study - Bloomberg&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;22&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;31&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;news&#x2F;688679&#x2F;amazon-ceo-andy-jassy-ai-efficiency&quot;&gt;Amazon CEO says it will cut jobs due to AI’s &#x27;efficiency&#x27; - The Verge&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;23&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;32&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thecon.ai&#x2F;&quot;&gt;The AI Con&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;24&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;33&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;anthonymoser.github.io&#x2F;writing&#x2F;ai&#x2F;haterdom&#x2F;2025&#x2F;08&#x2F;26&#x2F;i-am-an-ai-hater.html&quot;&gt;I Am An AI Hater - Anthony Moser&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;25&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;17&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;2024&#x2F;10&#x2F;23&#x2F;technology&#x2F;characterai-lawsuit-teen-suicide.html&quot;&gt;Can A.I. Be Blamed for a Teen’s Suicide? - New York Times&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;26&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;15&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mindmatters.ai&#x2F;2024&#x2F;04&#x2F;large-language-models-are-often-wrong-never-in-doubt&#x2F;&quot;&gt;Large Language Models Are Often Wrong, Never in Doubt - Gary Smith&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;27&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;29&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2025&#x2F;09&#x2F;04&#x2F;m365_copilot_uk_government&#x2F;&quot;&gt;UK government trial of M365 Copilot finds no clear productivity boost - The Register&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;28&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;19&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;2025&#x2F;08&#x2F;08&#x2F;technology&#x2F;ai-chatbots-delusions-chatgpt.html?ref=platformer.news&quot;&gt;Chatbots Can Go Into a Delusional Spiral. Here’s How It Happens - New York Times&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;29&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;26&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theguardian.com&#x2F;technology&#x2F;2025&#x2F;feb&#x2F;11&#x2F;uk-copyright-law-consultation-fixed-favour-ai-firms-peer-says&quot;&gt;UK copyright law consultation &#x27;fixed&#x27; in favour of AI firms, peer says - The Guardian&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;30&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;27&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theguardian.com&#x2F;technology&#x2F;2024&#x2F;dec&#x2F;17&#x2F;uk-proposes-letting-tech-firms-use-copyrighted-work-to-train-ai&quot;&gt;UK proposes letting tech firms use copyrighted work to train AI - The Guardian&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;31&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;22&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.schneier.com&#x2F;blog&#x2F;archives&#x2F;2023&#x2F;12&#x2F;ai-and-trust.html&quot;&gt;AI and Trust - Bruce Schneier&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;32&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;23&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@mark_stokes&#x2F;the-ai-starvation-loop-how-ai-is-starving-the-web-and-what-we-can-do-about-it-e0e567f13ad4&quot;&gt;The AI Starvation Loop - Mark Stokes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;33&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;14&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.cjr.org&#x2F;tow_center&#x2F;qa-uncovering-the-labor-exploitation-that-powers-ai.php&quot;&gt;Q&amp;amp;A: Uncovering the labor exploitation that powers AI - Columbia Journalism Review&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Converting a string to an int in Rust (for no reason)</title>
          <pubDate>Fri, 12 Dec 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/12/12/fizzbuzz-in-rust/</link>
          <guid>https://artificialworlds.net/blog/2025/12/12/fizzbuzz-in-rust/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/12/12/fizzbuzz-in-rust/">&lt;p&gt;How about a little light relief? Let&#x27;s convert a string to an int in Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Converting a string to an int in Rust (for no reason)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3NJ7dghstQtHsuZTFo3LZb&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I don&#x27;t use AI (expanded)</title>
          <pubDate>Wed, 10 Dec 2025 10:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/12/10/why-i-dont-use-ai-expanded/</link>
          <guid>https://artificialworlds.net/blog/2025/12/10/why-i-dont-use-ai-expanded/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/12/10/why-i-dont-use-ai-expanded/">&lt;p&gt;Soon after I wrote &lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;09&#x2F;03&#x2F;why-i-dont-use-ai&#x2F;&quot;&gt;Why I don&#x27;t use AI&lt;&#x2F;a&gt;, I
researched and submitted a significantly longer version to ACCU&#x27;s Overload
journal.&lt;&#x2F;p&gt;
&lt;p&gt;It has now been published! You can read it here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;journals&#x2F;overload&#x2F;33&#x2F;190&#x2F;balaam&#x2F;&quot;&gt;Why I don&#x27;t use AI
(Overload)&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It contains references to a lot more sources, and expands on my reasoning.&lt;&#x2F;p&gt;
&lt;p&gt;Update: the original was moved behind a paywall, so here is the full article
hosted here: &lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;12&#x2F;16&#x2F;why-i-dont-use-ai&#x2F;&quot;&gt;Why I don&#x27;t use AI&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Update 2: the paywall was removed, but I&#x27;ll leave the link here just in case.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Resizing and moving an encrypted LVM volume</title>
          <pubDate>Tue, 09 Dec 2025 10:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/12/09/resizing-encrypted-lvm/</link>
          <guid>https://artificialworlds.net/blog/2025/12/09/resizing-encrypted-lvm/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/12/09/resizing-encrypted-lvm/">&lt;p&gt;I use a LUKS-encrypted LVM setup for my laptop. Within the encrypted volume I have partitions for &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt; and I
guessed the sizes wrong, so I needed to change them.&lt;&#x2F;p&gt;
&lt;p&gt;I did various reading, and even resized some filesystems and partitions, before I discovered the answer that made it all
very easy for me.&lt;&#x2F;p&gt;
&lt;p&gt;Reboot into an Ubuntu live USB, do&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install partitionmanager
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;and then start &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;apps.kde.org&#x2F;en-gb&#x2F;partitionmanager&#x2F;&quot;&gt;KDE Partition Manager&lt;&#x2F;a&gt; from the menu.&lt;&#x2F;p&gt;
&lt;p&gt;I was able to resize and move partitions within my encrypted volume, and resizing filesystems was handled automatically
for me. It even handled the fact that my &lt;code&gt;&#x2F;&lt;&#x2F;code&gt; partition was before the &lt;code&gt;&#x2F;home&lt;&#x2F;code&gt; so there wasn&#x27;t room to expand it. I
&lt;em&gt;think&lt;&#x2F;em&gt; it moved it to the end, or maybe it doesn&#x27;t actually work the way I think it does.&lt;&#x2F;p&gt;
&lt;p&gt;Everything worked flawlessly and I am typing this on the machine which successfully booted after the changes, and shows
the correctly-resized partitions.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Implementing FizzBuzz in Rust (for no reason)</title>
          <pubDate>Sat, 29 Nov 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/11/29/fizzbuzz-in-rust/</link>
          <guid>https://artificialworlds.net/blog/2025/11/29/fizzbuzz-in-rust/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/11/29/fizzbuzz-in-rust/">&lt;p&gt;Things getting you down? Let&#x27;s implement FizzBuzz. It should be low stress
because it&#x27;s not a difficult task, but maybe we can think about some nice ways
to make it work.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Implementing FizzBuzz in Rust (for no reason)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4ZkRXaBgaXWTk6fwL5Zfp7&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Reversing a string in Rust (for no reason)</title>
          <pubDate>Thu, 13 Nov 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/11/13/reversing-a-string-in-rust/</link>
          <guid>https://artificialworlds.net/blog/2025/11/13/reversing-a-string-in-rust/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/11/13/reversing-a-string-in-rust/">&lt;p&gt;Feeling slightly exhausted by the world? Let&#x27;s reverse a string in Rust in a
needlessly complicated way. I was expecting to make a tiny simple video and
ended up going further into &lt;code&gt;unsafe&lt;&#x2F;code&gt; than I ever have before, which was an
unexpected treat. Let me know what I got wrong!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Reversing a string in Rust (for no reason)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2YJVJXsJLmN8zNRusaB6kg&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Follow me on mastodon: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 050: Parsing function definitions</title>
          <pubDate>Fri, 24 Oct 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/24/mini-rust-in-rust-050-parsing-function-def/</link>
          <guid>https://artificialworlds.net/blog/2025/10/24/mini-rust-in-rust-050-parsing-function-def/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/24/mini-rust-in-rust-050-parsing-function-def/">&lt;p&gt;To make real programs, we will need functions. We&#x27;ll start off by allowing you
to define functions that take no arguments.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 050: Parsing function definitions&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;t1a76XLM55csM6QRocbuoB&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    at0m
    &lt;span class=&quot;comment_date&quot;&gt;2026-05-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Looking forward to more episodes!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2026-05-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks at0m! I&#x27;m not sure many other people were keen, so I&#x27;m not planning
any more episodes yet, but you never know ... maybe I&#x27;ll get back into this one
day...&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Why do I have 2 passwords? How to talk about encryption in Matrix</title>
          <pubDate>Mon, 20 Oct 2025 13:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/20/why-do-i-have-two-passwords-video/</link>
          <guid>https://artificialworlds.net/blog/2025/10/20/why-do-i-have-two-passwords-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/20/why-do-i-have-two-passwords-video/">&lt;iframe width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;matrix-conf-2025-72671-why-do-i-have-2-passwords-how-to-talk-about-encryption-in-matrix&#x2F;oembed&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Most modern software applications give total trust to the service provider. End-to-end encrypted (E2EE) services are different: the service provider is a gateway, and the real trust is with other people.&lt;&#x2F;p&gt;
&lt;p&gt;This is unfamiliar, and can make using E2EE confusing. When you add in federation (meaning lots of different service providers) and a diverse set of client apps, trying to make Matrix&#x27;s encryption understandable is tricky.&lt;&#x2F;p&gt;
&lt;p&gt;It would help to have a shared set of words and definitions. In this talk I describe MSC4161, which attempts to establish a shared vocabulary, and then my own thoughts about some metaphors we can use to make these ideas easier to grasp.&lt;&#x2F;p&gt;
&lt;p&gt;End-to-end encryption (E2EE) is unfamiliar to people used to using modern applications: instead of handing over total trust to the server you connect to, E2EE makes the server a simple connector, and we only have to trust the people we are actually talking to.&lt;&#x2F;p&gt;
&lt;p&gt;It is quite common for people using Matrix to ask &quot;Why do I have 2 passwords?&quot;, referring to the username and password for logging in, and the recovery key.&lt;&#x2F;p&gt;
&lt;p&gt;I recently made a leap of understanding about this question: the reason is that there are two audiences: the password is for your homeserver, and the recovery key is for allowing you to talk to other people.&lt;&#x2F;p&gt;
&lt;p&gt;I began working on MSC4161 (&quot;Crypto terminology for non-technical users&quot;) because I believed we needed to standardise our vocabulary, but it quickly became clear that we need to do more than that: if we want to make Matrix easy to use, we need to agree on:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;what the main ideas are,&lt;&#x2F;li&gt;
&lt;li&gt;what words we use to describe them, and&lt;&#x2F;li&gt;
&lt;li&gt;what metaphors we use to explain.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In its current form, the MSC focusses on the first two. In this talk, I will summarise the ideas and words that are reasonably settled in the MSC and will hopefully make their way into the spec, and I will also introduce some ideas I have about the third item: how to explain Matrix&#x27;s E2EE using some metaphors that are intended to find the right level of explanation, and be more accurate than the metaphors we have used up to now.&lt;&#x2F;p&gt;
&lt;p&gt;We will go through the proposed common words:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Devices&#x2F;sessions and Identity&lt;&#x2F;li&gt;
&lt;li&gt;Verified users&lt;&#x2F;li&gt;
&lt;li&gt;Message keys, message history and key storage&lt;&#x2F;li&gt;
&lt;li&gt;Recovery and recovery keys&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Then we will look at some proposed metaphors:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;connect&quot; for logging in,&lt;&#x2F;li&gt;
&lt;li&gt;&quot;id card&quot; for identity, and&lt;&#x2F;li&gt;
&lt;li&gt;&quot;safe deposit box&quot; for recovery&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I will try to argue that these metaphors are at the right level of explanation, and they match more closely to what is really happening than the ideas we have used up to now.&lt;&#x2F;p&gt;
&lt;p&gt;Licensed to the public under https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Invisible Crypto: can Matrix be both secure and easy to use?</title>
          <pubDate>Mon, 20 Oct 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/20/invisible-crypto-video/</link>
          <guid>https://artificialworlds.net/blog/2025/10/20/invisible-crypto-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/20/invisible-crypto-video/">&lt;iframe width=&quot;1024&quot; height=&quot;576&quot; src=&quot;https:&#x2F;&#x2F;media.ccc.de&#x2F;v&#x2F;matrix-conf-2025-72625-invisible-crypto-can-matrix-be-both-secure-and-easy-to-use&#x2F;oembed&quot; frameborder=&quot;0&quot; allowfullscreen&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;The Invisible Crypto initiative intends to make Matrix easier to use by ensuring that encrypted messaging is secure by default, and the user is not bothered by irrelevant information.&lt;&#x2F;p&gt;
&lt;p&gt;In this talk we will give a status update, hopefully explaining why crypto needed to become slightly more visible on the journey towards making it disappear.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll go into some detail about what we&#x27;ve done (and why some of it makes things a little more noisy) and what we plan to do to really get there.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve been working on the Invisible Crypto initiative for over a year, and arguably things have got worse rather than better: some parts of Matrix crypto are more visible than they were before.&lt;&#x2F;p&gt;
&lt;p&gt;We will go into some detail about what we&#x27;ve done so far and why those things temporarily make more noise, but will eventually lead to a peaceful, hassle-free experience of encrypted messaging.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the coding has been done in matrix-rust-sdk and the Element clients, but the intention is for these efforts to serve as examples for other implementations.&lt;&#x2F;p&gt;
&lt;p&gt;Key goals of the initiative are to ignore &quot;insecure&quot; devices, and treat user identities as trust-on-first-use by default. To make these possible, we have worked on a lot of things that actually make crypto more visible:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Shields for messages from insecure devices&lt;&#x2F;li&gt;
&lt;li&gt;Encouraging device verification&lt;&#x2F;li&gt;
&lt;li&gt;Building a shared language to talk about crypto&lt;&#x2F;li&gt;
&lt;li&gt;Noticing and warning about devices with incomplete crypto information&lt;&#x2F;li&gt;
&lt;li&gt;Warnings about messages whose sender identity is unsure&lt;&#x2F;li&gt;
&lt;li&gt;Letting the user know when an identity changes&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;and we plan to continue with things that mostly make crypto less visible, while also becoming more secure:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Excluding insecure devices&lt;&#x2F;li&gt;
&lt;li&gt;Authenticated backup&lt;&#x2F;li&gt;
&lt;li&gt;Sharing identity updates between devices&lt;&#x2F;li&gt;
&lt;li&gt;Fixing bugs that allow a device&#x27;s crypto information to be incomplete&lt;&#x2F;li&gt;
&lt;li&gt;Showing identity changes in timelines instead of pop-ups&lt;&#x2F;li&gt;
&lt;li&gt;&quot;Dehydrated&quot; devices so messages received while logged out can be decrypted&lt;&#x2F;li&gt;
&lt;li&gt;Recovery key management&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Improving crypto-related code is always painstaking, but we hope to convince you that we are making steady progress, and demonstrate how you can help!&lt;&#x2F;p&gt;
&lt;p&gt;Licensed to the public under https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 049: Lexing an empty function</title>
          <pubDate>Fri, 17 Oct 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/17/mini-rust-in-rust-049-lexing-functions/</link>
          <guid>https://artificialworlds.net/blog/2025/10/17/mini-rust-in-rust-049-lexing-functions/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/17/mini-rust-in-rust-049-lexing-functions/">&lt;p&gt;The next big thing we need to be able to do is define functions. Let&#x27;s start by
lexing the &lt;code&gt;fn&lt;&#x2F;code&gt; keyword and brackets: we&#x27;re going to need both of those.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 049: Lexing an empty function&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;jPupBYspMEi1k6AqLZoGm3&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 048: Handling variable scope</title>
          <pubDate>Fri, 10 Oct 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/10/mini-rust-in-rust-048-scopes/</link>
          <guid>https://artificialworlds.net/blog/2025/10/10/mini-rust-in-rust-048-scopes/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/10/mini-rust-in-rust-048-scopes/">&lt;p&gt;Variables should be defined inside their curly braces and not elsewhere, and it
should be fine to &quot;shadow&quot; variables by defining new ones inside a smaller
scope.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 048: Handling variable scope&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;hQ8ZsssNtrKSboZFF5RFHj&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 047: Updating types as we get more information</title>
          <pubDate>Fri, 03 Oct 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/10/03/mini-rust-in-rust-047-update-types-later/</link>
          <guid>https://artificialworlds.net/blog/2025/10/03/mini-rust-in-rust-047-update-types-later/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/10/03/mini-rust-in-rust-047-update-types-later/">&lt;p&gt;If we know that &lt;code&gt;y&lt;&#x2F;code&gt; is an &lt;code&gt;f64&lt;&#x2F;code&gt;, when we see &lt;code&gt;x *= y&lt;&#x2F;code&gt;, we can guess that &lt;code&gt;x&lt;&#x2F;code&gt; is an &lt;code&gt;f64&lt;&#x2F;code&gt; too.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 047: Updating types as we get more information&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;8CrUoLNxPkYTZTz5AqWeeA&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Jez and Andy write a Parser</title>
          <pubDate>Fri, 26 Sep 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/09/26/jez-and-andy-write-a-parser/</link>
          <guid>https://artificialworlds.net/blog/2025/09/26/jez-and-andy-write-a-parser/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/09/26/jez-and-andy-write-a-parser/">&lt;p&gt;My friend Jez is back, and we make a start on a parser for the Cell language (in
Rust, of course).&lt;&#x2F;p&gt;
&lt;p&gt;You can find Jez at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;&quot;&gt;jezuk.co.uk&lt;&#x2F;a&gt; and follow him at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.me.uk&#x2F;@jezhiggins&quot;&gt;@jezhiggins@mastodon.me.uk&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Jez and Andy write a Parser&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;hEriRbWo4Q35h8K9xfUtGr&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Soon after we finished recording, Jez reported to me that he&#x27;d got it passing
all tests, and provided this code snippet:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fn next_node(&amp;amp;mut self, prev: Option&amp;lt;AST&amp;gt;) -&amp;gt; Option&amp;lt;AST&amp;gt; {
    match self.next_token() {
        Some(token) =&amp;gt; self.build_node(token, prev),
        None =&amp;gt; prev.and(make_ast_error(&amp;quot;Hit end of file - expected &amp;#39;;&amp;#39;.&amp;quot;)),
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Jez learns Rust by writing a Lexer</title>
          <pubDate>Fri, 19 Sep 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/09/19/jez-learns-rust/</link>
          <guid>https://artificialworlds.net/blog/2025/09/19/jez-learns-rust/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/09/19/jez-learns-rust/">&lt;p&gt;My friend Jez joins us and we talk about his new implementation of my Cell
programming language in Rust, which is his first from-scratch Rust project.&lt;&#x2F;p&gt;
&lt;p&gt;You can find Jez at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jezuk.co.uk&#x2F;&quot;&gt;jezuk.co.uk&lt;&#x2F;a&gt; and follow him at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.me.uk&#x2F;@jezhiggins&quot;&gt;@jezhiggins@mastodon.me.uk&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Jez learns Rust by writing a Lexer&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;uAKiFMeV7oiw9cR6QKgEFE&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 046: Type-checking mutations</title>
          <pubDate>Fri, 12 Sep 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/09/12/mini-rust-in-rust-046-type-check-mutations/</link>
          <guid>https://artificialworlds.net/blog/2025/09/12/mini-rust-in-rust-046-type-check-mutations/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/09/12/mini-rust-in-rust-046-type-check-mutations/">&lt;p&gt;When we see &lt;code&gt;x *= 1.3&lt;&#x2F;code&gt; we need to check that &lt;code&gt;x&lt;&#x2F;code&gt; is a float - otherwise this is a compile error.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 046: Type-checking mutations&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ruLm9ytt5rfMruvf4PmE1Y&quot; style=&quot;border: 0px;&quot; allow=&quot;fullscreen&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 045: Combining two types when we add things</title>
          <pubDate>Fri, 05 Sep 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/09/05/mini-rust-in-rust-045-combine-types-in-operation/</link>
          <guid>https://artificialworlds.net/blog/2025/09/05/mini-rust-in-rust-045-combine-types-in-operation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/09/05/mini-rust-in-rust-045-combine-types-in-operation/">&lt;p&gt;We had a weird hack in place to handle the type of an expression like &lt;code&gt;x + 3&lt;&#x2F;code&gt; but now we can do better. Let&#x27;s do it!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 045: Combining two types when we add things&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;qoQd275qXAyppc2tBVscsz&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I don&#x27;t use AI</title>
          <pubDate>Wed, 03 Sep 2025 10:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/09/03/why-i-dont-use-ai/</link>
          <guid>https://artificialworlds.net/blog/2025/09/03/why-i-dont-use-ai/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/09/03/why-i-dont-use-ai/">&lt;p&gt;I choose to avoid using &quot;AI&quot; (by which I mean
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Large_language_model&quot;&gt;Large Language Models&lt;&#x2F;a&gt;).
Here&#x27;s why:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They have a devastating &lt;strong&gt;environmental impact&lt;&#x2F;strong&gt;. &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;li&gt;They are trained by &lt;strong&gt;exploiting and traumatising millions of low-paid
workers&lt;&#x2F;strong&gt;. &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;li&gt;They produce &lt;strong&gt;biased and dangerously incorrect results&lt;&#x2F;strong&gt;. &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#6&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#7&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;li&gt;They &lt;strong&gt;unfairly use people&#x27;s creative work&lt;&#x2F;strong&gt;. &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#8&quot;&gt;8&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#9&quot;&gt;9&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are many other reasons, including that
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;metr.org&#x2F;blog&#x2F;2025-07-10-early-2025-ai-experienced-os-dev-study&#x2F;&quot;&gt;they don&#x27;t do what the billionaires claim they do&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bloomberg.com&#x2F;news&#x2F;articles&#x2F;2025-08-12&#x2F;ai-eroded-doctors-ability-to-spot-cancer-within-months-in-study?embedded-checkout=true&quot;&gt;they make you worse at your job&lt;&#x2F;a&gt;
and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;news&#x2F;688679&#x2F;amazon-ceo-andy-jassy-ai-efficiency&quot;&gt;people will lose their jobs because of them&lt;&#x2F;a&gt;.
(You can find out more via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thecon.ai&#x2F;&quot;&gt;The AI Con&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Yes, they &lt;em&gt;can&lt;&#x2F;em&gt; do some things effectively (e.g. making very convincing fake
videos). I choose to avoid them where I can, for the above reasons.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: you can read an expanded version of this article with lots more references here:
&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;12&#x2F;16&#x2F;why-i-dont-use-ai&#x2F;&quot;&gt;Why I don&#x27;t use AI&lt;&#x2F;a&gt; (or the original journal
article that was put behind a paywall: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;journals&#x2F;overload&#x2F;33&#x2F;190&#x2F;balaam&#x2F;&quot;&gt;Why I don&#x27;t use AI
(Overload)&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;(Almost all the links I provide were researched by Anthony Moser for their
article
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;anthonymoser.github.io&#x2F;writing&#x2F;ai&#x2F;haterdom&#x2F;2025&#x2F;08&#x2F;26&#x2F;i-am-an-ai-hater.html&quot;&gt;I Am An AI Hater&lt;&#x2F;a&gt; - thank you very much!)&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.teenvogue.com&#x2F;story&#x2F;chatgpt-is-everywhere-environmental-costs-oped&quot;&gt;ChatGPT Is Everywhere - Why Aren&#x27;t We Talking About Its Environmental Costs? - Teen Vogue&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;articles&#x2F;cy8gy7lv448o&quot;&gt;&#x27;I can&#x27;t drink the water&#x27; - life next to a US data centre - BBC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.noemamag.com&#x2F;the-exploited-labor-behind-artificial-intelligence&#x2F;&quot;&gt;The Exploited Labor Behind Artificial Intelligence - Noema&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;4&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wired.com&#x2F;story&#x2F;millions-of-workers-are-training-ai-models-for-pennies&#x2F;&quot;&gt;Millions of Workers Are Training AI Models for Pennies - Wired&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;5&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;apnews.com&#x2F;article&#x2F;ai-artificial-intelligence-health-business-90020cdf5fa16c79ca2e5b6c4c9bbb14&quot;&gt;Researchers say an AI-powered transcription tool used in hospitals invents things no one ever said - AP News&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;6&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.npr.org&#x2F;2025&#x2F;07&#x2F;09&#x2F;nx-s1-5462609&#x2F;grok-elon-musk-antisemitic-racist-content&quot;&gt;Elon Musk&#x27;s AI chatbot, Grok, started calling itself &#x27;MechaHitler&#x27; - NPR&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;7&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nytimes.com&#x2F;2025&#x2F;08&#x2F;26&#x2F;technology&#x2F;chatgpt-openai-suicide.html&quot;&gt;A Teen Was Suicidal. ChatGPT Was the Friend He Confided In - New York Times&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;8&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;8&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jskfellows.stanford.edu&#x2F;theft-is-not-fair-use-474e11f0d063&quot;&gt;Theft is not fair use - David Carson&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;9&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;9&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;technology-67826601&quot;&gt;New York Times sues Microsoft and OpenAI for &#x27;billions&#x27; - BBC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 044: Finding the type of a symbol</title>
          <pubDate>Fri, 29 Aug 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/08/29/mini-rust-in-rust-044-types-of-symbols/</link>
          <guid>https://artificialworlds.net/blog/2025/08/29/mini-rust-in-rust-044-types-of-symbols/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/08/29/mini-rust-in-rust-044-types-of-symbols/">&lt;p&gt;If we write &lt;code&gt;let x: i32 = 3;&lt;&#x2F;code&gt; then we should know that &lt;code&gt;x&lt;&#x2F;code&gt; is an &lt;code&gt;i32&lt;&#x2F;code&gt;, right? Let&#x27;s make it so!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 044: Finding the type of a symbol&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;hLtxBxUTiaKhJH2zy9RFEY&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 043: Expressions on the left of operators</title>
          <pubDate>Fri, 22 Aug 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/08/22/mini-rust-in-rust-043-expression-on-left-of-operation/</link>
          <guid>https://artificialworlds.net/blog/2025/08/22/mini-rust-in-rust-043-expression-on-left-of-operation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/08/22/mini-rust-in-rust-043-expression-on-left-of-operation/">&lt;p&gt;The final (for now) side mission before we get back onto type-checking variables: allowing complex expressions to exist before operators.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 043: Expressions on the left of operators&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;5fev2Zj98BFSZzhWerUQEE&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 042: An unexpected token means stop this expression</title>
          <pubDate>Fri, 15 Aug 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/08/15/mini-rust-in-rust-042-unexpected-token-means-stop-expression/</link>
          <guid>https://artificialworlds.net/blog/2025/08/15/mini-rust-in-rust-042-unexpected-token-means-stop-expression/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/08/15/mini-rust-in-rust-042-unexpected-token-means-stop-expression/">&lt;p&gt;We&#x27;re on our way towards type-checking variables, but first we take another detour into parsing expressions: if we hit an unexpected token, we should backtrack and return what we found so far, not just stop with an error.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 042: An unexpected token means stop this expression&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;tTKFh8R7eGx6eUvPPX2he3&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jezhiggins
    &lt;span class=&quot;comment_date&quot;&gt;2025-11-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;That was a journey!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 041: While conditions that are not operations</title>
          <pubDate>Fri, 08 Aug 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/08/08/mini-rust-in-rust-041-while-cond-not-operation/</link>
          <guid>https://artificialworlds.net/blog/2025/08/08/mini-rust-in-rust-041-while-cond-not-operation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/08/08/mini-rust-in-rust-041-while-cond-not-operation/">&lt;p&gt;We are starting down a road towards type checking variables, but we go on a detour to make sure we can parse the simplest possible condition in a while loop, which it turns out we missed.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 041: While conditions that are not operations&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;orvwWaWsqzhme7p1kE2yrq&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 040: While conditions should be bool</title>
          <pubDate>Fri, 01 Aug 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/08/01/mini-rust-in-rust-040-while-condition-should-be-bool/</link>
          <guid>https://artificialworlds.net/blog/2025/08/01/mini-rust-in-rust-040-while-condition-should-be-bool/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/08/01/mini-rust-in-rust-040-while-condition-should-be-bool/">&lt;p&gt;Working up towards some difficult type-checking, we do some easy type-checking: the condition part of a while condition should be a boolean, and to get there we need to improve the type we give to the result of an operation.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 040: While conditions should be bool&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;kJL6yxW3yBDiNE9kqfeJyk&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 039: Spans should be ranges</title>
          <pubDate>Fri, 25 Jul 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/07/25/mini-rust-in-rust-039-span-as-range/</link>
          <guid>https://artificialworlds.net/blog/2025/07/25/mini-rust-in-rust-039-span-as-range/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/07/25/mini-rust-in-rust-039-span-as-range/">&lt;p&gt;We&#x27;ve been tracking the location of a token in the file using a pair of numbers, but Rust has a better way: a &lt;code&gt;Range&lt;&#x2F;code&gt;. After some debate and wandering, we make the code a little better with this change.&lt;&#x2F;p&gt;
&lt;p&gt;The interlude music is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freemusicarchive.org&#x2F;music&#x2F;hunk-golden&#x2F;single&#x2F;blood-collateral-otherwise&#x2F;&quot;&gt;Blood, Collateral, &amp;amp; Otherwise&lt;&#x2F;a&gt; by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;freemusicarchive.org&#x2F;music&#x2F;hunk-golden&#x2F;&quot;&gt;Hunk Golden&lt;&#x2F;a&gt;, used under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by&#x2F;4.0&quot;&gt;Attribution 4.0 International License&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 039: Spans should be ranges&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;9BM3kMPErvgB8QrdErU6gY&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 038: Avoiding a String allocation for each token</title>
          <pubDate>Fri, 18 Jul 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/07/18/mini-rust-in-rust-038-no-strings-in-tokens/</link>
          <guid>https://artificialworlds.net/blog/2025/07/18/mini-rust-in-rust-038-no-strings-in-tokens/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/07/18/mini-rust-in-rust-038-no-strings-in-tokens/">&lt;p&gt;Last time I described what I tried to do with removing the extra String stored
in each Token. This time, having figured out a workaround, we go ahead and
remove them. Of course, it&#x27;s more complicated than we expected.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 038: Avoiding a String allocation for each token&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;qxFSknfkjgAoVPNe1abMuk&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 037: Complex expressions in mutation statements</title>
          <pubDate>Fri, 11 Jul 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/07/11/mini-rust-in-rust-037-mutation-expressions/</link>
          <guid>https://artificialworlds.net/blog/2025/07/11/mini-rust-in-rust-037-mutation-expressions/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/07/11/mini-rust-in-rust-037-mutation-expressions/">&lt;p&gt;We left a lot of gaps while we were building towards some working code. The
first to tackle is allowing complex expressions on the right-hand side of
mutation statements.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 037: Complex expressions in mutation statements&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;r6h6ae8VDUS6L7ssx8RMPM&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 036: Evaluating code blocks</title>
          <pubDate>Fri, 04 Jul 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/07/04/mini-rust-in-rust-036-eval-block/</link>
          <guid>https://artificialworlds.net/blog/2025/07/04/mini-rust-in-rust-036-eval-block/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/07/04/mini-rust-in-rust-036-eval-block/">&lt;p&gt;Blocks of code surrounded by braces are valid statements in Rust - it&#x27;s just a
small change to our existing code to be able to evaluate them.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 036: Evaluating code blocks&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;eruV7mThTS9HqPg81Rt4EH&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 035: Tidying up the evaluator</title>
          <pubDate>Fri, 27 Jun 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/06/27/mini-rust-in-rust-035-tidying-evaluator/</link>
          <guid>https://artificialworlds.net/blog/2025/06/27/mini-rust-in-rust-035-tidying-evaluator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/06/27/mini-rust-in-rust-035-tidying-evaluator/">&lt;p&gt;We left quite a few messes behind in our evaluator - let&#x27;s get rid of some
&lt;code&gt;ref&lt;&#x2F;code&gt;s, stop returning multiple values in our test code, and handle blocks
similarly to other code.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 035: Tidying up the evaluator&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;x7KYHTFp1pLPmCVcbpCy6G&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 034: Our first program, actually working</title>
          <pubDate>Fri, 20 Jun 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/06/20/mini-rust-in-rust-034-evaluating-while/</link>
          <guid>https://artificialworlds.net/blog/2025/06/20/mini-rust-in-rust-034-evaluating-while/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/06/20/mini-rust-in-rust-034-evaluating-while/">&lt;p&gt;It&#x27;s finally here - our mini-program to calculate factorials, featuring a while
loop and mutable variables, is working!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 034: Our first program, actually working&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;oFYHb2Q4w7eYFhE1TX28m2&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 033: Evaluating operators like plusequals and timesequals</title>
          <pubDate>Fri, 13 Jun 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/06/13/mini-rust-in-rust-033-evaluating-plusequals/</link>
          <guid>https://artificialworlds.net/blog/2025/06/13/mini-rust-in-rust-033-evaluating-plusequals/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/06/13/mini-rust-in-rust-033-evaluating-plusequals/">&lt;p&gt;Our while loop is close ... first we need to be actually calculate new values
for variables when we see a += or *= operator.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 033: Evaluating operators like += and *=&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;doNQbAwfR6NXwANpfaEYdf&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 032: Supporting a Unit type and type checking mutations</title>
          <pubDate>Fri, 06 Jun 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/06/06/mini-rust-in-rust-032-type-checking-mutation-and-unit/</link>
          <guid>https://artificialworlds.net/blog/2025/06/06/mini-rust-in-rust-032-type-checking-mutation-and-unit/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/06/06/mini-rust-in-rust-032-type-checking-mutation-and-unit/">&lt;p&gt;We&#x27;ve been putting off creating a Unit type, (), and we need our type checker to
be OK with += etc. We are getting excitingly close to having a working while
loop!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 032: Supporting a Unit type and type checking mutations&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;tLmRuPcXJcRb2M9eVBTWrM&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 031: Getting &quot;while&quot; through the type checker</title>
          <pubDate>Fri, 30 May 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/30/mini-rust-in-rust-031-type-checking-while/</link>
          <guid>https://artificialworlds.net/blog/2025/05/30/mini-rust-in-rust-031-type-checking-while/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/30/mini-rust-in-rust-031-type-checking-while/">&lt;p&gt;Our while loop now parses, but it doesn&#x27;t make its way through the type-checker
unscathed. We&#x27;re not quite ready to do real type checking here, but we need to
pass this code through so we can actually run it!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 031: Getting &amp;quot;while&amp;quot; through the type checker&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2kCpfiGcE4Eu9BqGWzrP7e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 030: Expressions after while loops</title>
          <pubDate>Fri, 23 May 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/23/mini-rust-in-rust-030-expression-after-while/</link>
          <guid>https://artificialworlds.net/blog/2025/05/23/mini-rust-in-rust-030-expression-after-while/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/23/mini-rust-in-rust-030-expression-after-while/">&lt;p&gt;It turns out we are making a bad assumption in our parser: that every statement
ends with a semicolon. That&#x27;s not quite right - for example, while loops can
form statements, but they don&#x27;t need to be ended with a semi-colon. Let&#x27;s fix
the bug!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 030: Expressions after while loops&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;18RvsGVYSMwEgbnURBgTfY&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 029: Parsing plus-equals</title>
          <pubDate>Fri, 16 May 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/16/mini-rust-in-rust-029-parsing-plusequals/</link>
          <guid>https://artificialworlds.net/blog/2025/05/16/mini-rust-in-rust-029-parsing-plusequals/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/16/mini-rust-in-rust-029-parsing-plusequals/">&lt;p&gt;This is the first time we are handling modifying variables, so our current
&quot;assignment&quot; parser, which looks for a &lt;code&gt;let&lt;&#x2F;code&gt; keyword is no help - we need a new
bit of parsing for modification statements.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 029: Parsing plus-equals&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;g13j3x6cDAKe3iH5zBWVVA&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 028: Lexing plus-equals</title>
          <pubDate>Fri, 09 May 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/09/mini-rust-in-rust-028-lexing-plusequals/</link>
          <guid>https://artificialworlds.net/blog/2025/05/09/mini-rust-in-rust-028-lexing-plusequals/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/09/mini-rust-in-rust-028-lexing-plusequals/">&lt;p&gt;Adding another two-character operator to our lexer, which is surprisingly more
complicated than I expected.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 028: Lexing plus-equals&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;rM722SMGVoRbXVXg65Gpm8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jezhiggins
    &lt;span class=&quot;comment_date&quot;&gt;2025-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Not cross, just disappointed&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2025-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Not intimidated, just devastated.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Standing again for the Matrix Governing Board</title>
          <pubDate>Wed, 07 May 2025 10:30:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/07/standing-again-for-matrix-governing-board/</link>
          <guid>https://artificialworlds.net/blog/2025/05/07/standing-again-for-matrix-governing-board/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/07/standing-again-for-matrix-governing-board/">&lt;p&gt;I am standing for re-election to the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;foundation&#x2F;governing-board&#x2F;&quot;&gt;Matrix Governing Board&lt;&#x2F;a&gt;
(see the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;foundation&#x2F;governing-board-elections&#x2F;2025&#x2F;&quot;&gt;election announcement&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;The Governing Board advises and oversees the work of the Matrix Foundation. I
will stand as an Individual Member again, not representing a company or project.&lt;&#x2F;p&gt;
&lt;p&gt;Contents:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#purpose-of-the-board&quot;&gt;Purpose of the board&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#progress-so-far&quot;&gt;Progress so far&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#my-qualifications-and-interests&quot;&gt;My qualifications and interests&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#my-biases&quot;&gt;My biases&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#my-vision&quot;&gt;My vision&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2025&#x2F;05&#x2F;07&#x2F;standing-again-for-matrix-governing-board&#x2F;#my-priorities&quot;&gt;My priorities&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;purpose-of-the-board&quot;&gt;Purpose of the board&lt;&#x2F;h2&gt;
&lt;p&gt;The board operates on an advisory basis, but is responsible for approving
budgets, projects and partnerships that are significant to the future
of the foundation.&lt;&#x2F;p&gt;
&lt;p&gt;I see the Governing Board as having two major purposes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Speaking up if the foundation starts down the wrong course.&lt;&#x2F;strong&gt; If the
foundation makes choices that are harmful to Matrix or the community, the
board can shout about it (but not actually block it).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Guiding direction for the best interests of the community.&lt;&#x2F;strong&gt; The board
consists of people with personal and professional interest in the success of
Matrix, and with expertise in many areas, so it can provide advice and
guidance that help the foundation channel its funding and effort towards that
success.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;progress-so-far&quot;&gt;Progress so far&lt;&#x2F;h2&gt;
&lt;p&gt;The board has existed for one year, and progress has felt very slow. Lots of
processes have been hammered out, and some committees have been established,
which are where most of the real work will happen.&lt;&#x2F;p&gt;
&lt;p&gt;It is encouraging to see work being started co-ordinating with community efforts
on trust and safety.&lt;&#x2F;p&gt;
&lt;p&gt;I hope that the pace will pick up this year, and the board will start to help
organise community initiatives, and speak up about decisions being made by the
foundation.&lt;&#x2F;p&gt;
&lt;p&gt;I have joined the Fundraising and Finance committee, where my main interest is
in the finance part. I want to help the foundation face the reality of the
current funding shortfall and take action to address it. I have been able to ask
questions about this issue and received some answers, including about ways to
help make the matrix.org homeserver more self-sustaining, but the way forward is
far from clear, and time is short.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-qualifications-and-interests&quot;&gt;My qualifications and interests&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;I am a long-standing &lt;strong&gt;contributor to many Free&#x2F;Open Source projects&lt;&#x2F;strong&gt;. My
contribution history dates back to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;FreeGuide&#x2F;commit&#x2F;74e185826b778ef0cb7a758bf6016300364f7356&quot;&gt;at least 2001&lt;&#x2F;a&gt;
and I have been an enthusiastic supporter of the principles of Software
Freedom for many years.&lt;&#x2F;li&gt;
&lt;li&gt;I am a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&#x2F;sponsors&#x2F;#sustainers&quot;&gt;sustainer&lt;&#x2F;a&gt; of the
&lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&quot;&gt;Software Freedom Conservancy&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; as well as a
Matrix Foundation individual member.&lt;&#x2F;li&gt;
&lt;li&gt;As part of my work for Element I have contributed across many
&lt;strong&gt;Matrix projects&lt;&#x2F;strong&gt; including
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;pulls?q=+is%3Apr+author%3Aandybalaam+&quot;&gt;MSCs&lt;&#x2F;a&gt;
and the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;spec&lt;&#x2F;a&gt;,
the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-js-sdk&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;JS SDK&lt;&#x2F;a&gt;
and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-react-sdk&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;React SDK&lt;&#x2F;a&gt;,
the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rust-sdk&#x2F;pulls?q=author%3Aandybalaam+&quot;&gt;Rust SDK&lt;&#x2F;a&gt;,
the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;complement-crypto&#x2F;pulls?q=is%3Apr+author%3Aandybalaam+&quot;&gt;Crypto tests&lt;&#x2F;a&gt;
and the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rich-text-editor&#x2F;pulls?q=is%3Apr+author%3Aandybalaam+&quot;&gt;rich text editor&lt;&#x2F;a&gt;,
along with many others.&lt;&#x2F;li&gt;
&lt;li&gt;I am a &lt;strong&gt;good communicator&lt;&#x2F;strong&gt;, and have made many popular
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;a&#x2F;andybalaam&#x2F;video-channels&quot;&gt;programming videos&lt;&#x2F;a&gt;
(released under Free Culture licenses).&lt;&#x2F;li&gt;
&lt;li&gt;I am known in the places I have worked as a &lt;strong&gt;highly collaborative&lt;&#x2F;strong&gt; team
member, bringing organisational abilities and people skills along with my
technical expertise.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can find out more about me at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-biases&quot;&gt;My biases&lt;&#x2F;h2&gt;
&lt;p&gt;I am an employee of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;element.io&#x2F;&quot;&gt;Element&lt;&#x2F;a&gt;, meaning it is in my interest
for Element to prosper. Since one of the purposes of the Governing Board is to
increase the independence of the Matrix Foundation from Element, this could be a
downside to my involvement.&lt;&#x2F;p&gt;
&lt;p&gt;I have a long work and free software history outside Element which I think will
help me balance the different interests, and I commit to remove myself from
decisions whenever I think that my employment situation might cause a conflict
of interest. This means if I think on a particular topic Element&#x27;s interests
might be in conflict with the foundation&#x27;s I will remove myself from the
decision-making on that topic and make a short statement explaining why.&lt;&#x2F;p&gt;
&lt;p&gt;In practice, I believe that Element is a critical supporter of Matrix, and the
purpose of its founding was to make Matrix successful, so I expect that in most
cases there will be no conflict. (It&#x27;s worth noting that if you disagree with
that, you should probably vote against me!)&lt;&#x2F;p&gt;
&lt;p&gt;My other biases are towards human thriving, Software Freedom, open standards. I
like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Copyleft&quot;&gt;Copyleft&lt;&#x2F;a&gt; licenses, I prefer to
avoid the use of non-free platforms (e.g. GitHub) and I am normally against
strong &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Contributor_License_Agreement&quot;&gt;CLA&lt;&#x2F;a&gt;s.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-vision&quot;&gt;My vision&lt;&#x2F;h2&gt;
&lt;p&gt;I think human thriving requires a conscious focus on nurturing our communities
towards being more accepting of difference and more kind to each other. The
modern tech and political environments often work against this, and we should
resist them where they do. In particular, we should start with an assumption of
good faith, and protect each other from harm. These two principles are often in
conflict, so they require careful application.&lt;&#x2F;p&gt;
&lt;p&gt;I am an enthusiastic supporter of Matrix because I can imagine a world where
everyone can communicate securely and privately with their friends and family,
companies, medical services, governments and anyone else, free of snooping and
manipulation by powerful individual companies. In this world, we can talk
privately, do business, and administrate our lives without worrying about who is
excluded, who is listening, and whether we are the product. It&#x27;s a little
miracle that so many people have access to email, and it&#x27;s time to level up to
real-time, secure communication that is free from unwanted control and
influence.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-priorities&quot;&gt;My priorities&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I want people to thrive.&lt;&#x2F;strong&gt; Trustworthy technology is a small but significant
part of that, and I have an ambition for Matrix to be safe, inclusive,
private, and available to all. For that to happen, Matrix needs to last, and
fulfill its potential.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;I want Matrix to last.&lt;&#x2F;strong&gt; When I look at the success of the Linux project, I
see years of steady work paying off in the long term. The project put its
attention into making an excellent piece of software, and &quot;taking over the
world&quot; came slowly and naturally as a by-product of that. I think Matrix
should take the same approach: steadily working on creating an excellent open
standard and excellent implementations of it. Taking over the world (which is
absolutely my ambition!) will inevitably come if the product is good enough,
because the advantages of openness are so strong.&lt;&#x2F;li&gt;
&lt;li&gt;For Matrix to last, the foundation needs &lt;strong&gt;financial independence&lt;&#x2F;strong&gt;. I want to
see the foundation raise more money, both from individuals and from corporate
and government users. I&#x27;d like to see the foundation itself fund development
of the standard and some implementations. This would help development focus on
the features that are of most benefit to the whole community, and, critically,
it would support safety, maintenance and high quality.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you like the idea of my voice helping to guide the Matrix Foundation, please
vote for me!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Testing nonexistence</title>
          <pubDate>Tue, 06 May 2025 13:08:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/06/testing-nonexistence/</link>
          <guid>https://artificialworlds.net/blog/2025/05/06/testing-nonexistence/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/06/testing-nonexistence/">&lt;h3 id=&quot;testing-that-something-is-not-there-can-be-unreliable&quot;&gt;Testing that something is not there can be unreliable&lt;&#x2F;h3&gt;
&lt;p&gt;Today I am writing a test that something does not happen, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Given that the A setting is &amp;quot;off&amp;quot;

When I start the app

Then the warning about A being on IS NOT displayed
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s quite hard to check that something is not displayed in a robust way.
Initially it&#x27;s fine:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;assert!(page.does_not_contain(&amp;quot;Setting A should normally be turned off!&amp;quot;));
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But later if we change the wording of the warning, then this test becomes
useless: we could introduce a bug that makes the warning appear, and this test
would still pass, because it&#x27;s looking for the wrong text.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pair-it-with-testing-that-something-is-there&quot;&gt;Pair it with testing that something is there&lt;&#x2F;h3&gt;
&lt;p&gt;The best way I have found to guard against this is to make sure every &quot;not
there&quot; test is paired with an &quot;is there&quot; test as a companion:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Given that the A setting is &amp;quot;on&amp;quot;

When I start the app

Then the warning about A being on IS displayed
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now when we change the wording of the warning, this positive test fails, and we
can update both tests to use the new wording.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;so-it-s-all-fine&quot;&gt;So it&#x27;s all fine?&lt;&#x2F;h3&gt;
&lt;p&gt;But, of course, some poor programmer who was working on changing &quot;normally&quot; to
&quot;usually&quot; across 800 source code files will probably not be thinking much about
this pair of tests, and will be perturbing the code until all 10,800 tests pass,
and could easily miss the pairing.&lt;&#x2F;p&gt;
&lt;p&gt;They might fix the wording in the &quot;is there&quot; test, and miss it in the &quot;not
there&quot; test, making it useless again.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-to-make-this-better&quot;&gt;How to make this better&lt;&#x2F;h3&gt;
&lt;p&gt;It might be nice to establish a naming convention to show that tests are paired,
to help avoid this problem. Suggestions?&lt;&#x2F;p&gt;
&lt;p&gt;Even better, can we make some kind of &quot;existence determined by X&quot; test that
merges this pair of tests into a single one? Something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;Given that the A setting can be either &amp;quot;on&amp;quot; or &amp;quot;off&amp;quot;

When I start the app

Then the warning is controlled by A:
    &amp;quot;on&amp;quot;  = displayed
    &amp;quot;off&amp;quot; = not displayed
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;m not aware of this pattern being used anywhere. Anyone? What would the code
in the test look like?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 027: Parsing blocks of code</title>
          <pubDate>Fri, 02 May 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/05/02/mini-rust-in-rust-027-parsing-blocks/</link>
          <guid>https://artificialworlds.net/blog/2025/05/02/mini-rust-in-rust-027-parsing-blocks/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/05/02/mini-rust-in-rust-027-parsing-blocks/">&lt;p&gt;We can already parse empty &lt;code&gt;while&lt;&#x2F;code&gt; loops, but now we figure out how to parse the
block of code that forms the body of the loop. It should be useful for other
things including (eventually) function bodies.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 027: Parsing blocks of code&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;wmEKYWj2euNC6nXsNXv9Yz&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jezhiggins
    &lt;span class=&quot;comment_date&quot;&gt;2025-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Blimey Andy, that was journey&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2025-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Yeah, am I ever going to stop?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 026: Parsing an empty while loop</title>
          <pubDate>Fri, 25 Apr 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/04/25/mini-rust-in-rust-026-parsing-empty-while/</link>
          <guid>https://artificialworlds.net/blog/2025/04/25/mini-rust-in-rust-026-parsing-empty-while/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/04/25/mini-rust-in-rust-026-parsing-empty-while/">&lt;p&gt;The little real-world snippet we are trying to get working includes a &lt;code&gt;while&lt;&#x2F;code&gt;
loop - let&#x27;s try to get it to parse. For now, only if the actual loop body is
empty.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 026: Parsing an empty while loop&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;jfskCFWJCzapTvgSbHpy3v&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 025: Parsing let mut</title>
          <pubDate>Fri, 18 Apr 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/04/18/mini-rust-in-rust-025-parsing-mut/</link>
          <guid>https://artificialworlds.net/blog/2025/04/18/mini-rust-in-rust-025-parsing-mut/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/04/18/mini-rust-in-rust-025-parsing-mut/">&lt;p&gt;Continuing trying to get the simplest bit of real code we could think of to
work, we need to finish off less-than-or-equals, and also make &lt;code&gt;let mut&lt;&#x2F;code&gt; work.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 025: Parsing let mut&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;tAdcLqcfvvffXWi2erJJGv&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 024: Lexing the &lt;= sign</title>
          <pubDate>Fri, 11 Apr 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/04/11/mini-rust-in-rust-024-lexing-lte/</link>
          <guid>https://artificialworlds.net/blog/2025/04/11/mini-rust-in-rust-024-lexing-lte/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/04/11/mini-rust-in-rust-024-lexing-lte/">&lt;p&gt;Now we&#x27;re going to try and get a little code snippet that calculates factorials
to work. First up: lexing the &lt;code&gt;&amp;lt;=&lt;&#x2F;code&gt; sign!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 024: Lexing the &lt;= sign&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;jY9vC5ZaSQBPTPhoVZGi4p&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 023: Refactor to share structure of the syntax tree</title>
          <pubDate>Fri, 04 Apr 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/04/04/mini-rust-in-rust-023-share-syntax-tree-structure/</link>
          <guid>https://artificialworlds.net/blog/2025/04/04/mini-rust-in-rust-023-share-syntax-tree-structure/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/04/04/mini-rust-in-rust-023-share-syntax-tree-structure/">&lt;p&gt;We are repeating ourselves with the &lt;code&gt;SyntaxTree&lt;&#x2F;code&gt; and &lt;code&gt;TypedSyntaxTree&lt;&#x2F;code&gt; enums.
I thought the solution would be too complex, but on reflection it&#x27;s fine, so
let&#x27;s share that code.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 023: Refactor to share structure of the syntax tree&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0bd1399c-4390-4fc2-b833-ae5e8e73537d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust tracing basic setup</title>
          <pubDate>Sat, 29 Mar 2025 11:15:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/03/29/rust-tracing-basic-setup/</link>
          <guid>https://artificialworlds.net/blog/2025/03/29/rust-tracing-basic-setup/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/03/29/rust-tracing-basic-setup/">&lt;p&gt;I spent ages yesterday trying to figure out how to use Rust&#x27;s
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;tracing&quot;&gt;tracing&lt;&#x2F;a&gt; library (and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;tracing-subscriber&quot;&gt;tracing-subscriber&lt;&#x2F;a&gt;) for a very
simple logging use-case.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to use &lt;code&gt;tracing&lt;&#x2F;code&gt; because a) it&#x27;s rapidly becoming standard everywhere
and b) I was using libraries that used it.&lt;&#x2F;p&gt;
&lt;p&gt;I found it very hard to find a simple example that either takes its log level
from the &lt;code&gt;RUST_LOG&lt;&#x2F;code&gt; environment variable, or uses a non-trivial default. Here&#x27;s
what I came up with:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;$ cargo add tracing
$ cargo add tracing-subscriber --features env-filter
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;&lt;code&gt;use tracing_subscriber::FmtSubscriber;

fn main() {
    const DEFAULT_LOGGING: &amp;amp;str = &amp;quot;myprogram=info,warn&amp;quot;;

    let rust_log = std::env::var(&amp;quot;RUST_LOG&amp;quot;)
        .ok()
        .and_then(|s| if s.is_empty() { None } else { Some(s) })
        .unwrap_or_else(|| DEFAULT_LOGGING.to_owned());

    tracing::subscriber::set_global_default(
        FmtSubscriber::builder().with_env_filter(rust_log).finish(),
    )
    .expect(&amp;quot;tracing setup failed&amp;quot;);

    &#x2F;&#x2F; The rest of my program here ...
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;myprogram=info,warn&lt;&#x2F;code&gt; part sets the log level of my project to &lt;code&gt;info&lt;&#x2F;code&gt; and
any libraries I use to &lt;code&gt;warn&lt;&#x2F;code&gt;. You&#x27;ll need to replace &lt;code&gt;myprogram&lt;&#x2F;code&gt; with the name
of your project.&lt;&#x2F;p&gt;
&lt;p&gt;This sets a default logging level which can be overridden by launching the
program with e.g. &lt;code&gt;RUST_LOG=debug .&#x2F;myprogram&lt;&#x2F;code&gt; to set the level to &lt;code&gt;debug&lt;&#x2F;code&gt;
everywhere or e.g. &lt;code&gt;RUST_LOG=&quot;myproject=debug,warn&quot; .&#x2F;myprogram&lt;&#x2F;code&gt; to do something
more clever.&lt;&#x2F;p&gt;
&lt;p&gt;(The format of these comma-separated lists of directives are kind of documented
at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tracing-fmt&#x2F;latest&#x2F;tracing_fmt&#x2F;struct.Builder.html#method.with_env_filter&quot;&gt;tracing_fmt::Builder::with_env_filter&lt;&#x2F;a&gt;
and
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;tracing-subscriber&#x2F;latest&#x2F;tracing_subscriber&#x2F;filter&#x2F;struct.EnvFilter.html#directives&quot;&gt;EnvFilter&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Once that&#x27;s done I can log using code like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;use tracing::{debug, info};

pub fn myfun() {
    let x = 3;
    info!(&amp;quot;The number is {x}&amp;quot;);
    debug!(&amp;quot;DEBUG ONLY&amp;quot;);
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If anyone knows a simpler way to achieve these results, let me know. I spent a
long time fiddling with &lt;code&gt;EnvFilter::with_default_directive&lt;&#x2F;code&gt; and similar stuff
before I decided I had to check the contents of &lt;code&gt;RUST_LOG&lt;&#x2F;code&gt; manually if I wanted
a more complex default (not a single directive).&lt;&#x2F;p&gt;
&lt;p&gt;Side note: wow, &lt;code&gt;tracing_subscriber&lt;&#x2F;code&gt; is hard to use! I couldn&#x27;t find any simple
examples - links welcome.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2026-02-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great, I hope it’s useful!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ghenry
    &lt;span class=&quot;comment_date&quot;&gt;2026-02-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Stealing this for use in OneTalker 👌 Thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 022: Stop lying about types in errors</title>
          <pubDate>Fri, 28 Mar 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/03/28/mini-rust-in-rust-022-types-in-errors/</link>
          <guid>https://artificialworlds.net/blog/2025/03/28/mini-rust-in-rust-022-types-in-errors/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/03/28/mini-rust-in-rust-022-types-in-errors/">&lt;p&gt;Now that we have the types we calculated in our syntax tree, use them to stop
faking it in error messages - replace a hard-coded &quot;integer&quot; with the actual
type of the expression we are talking about.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 022: Stop lying about types in errors&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fba3eada-2fe7-4851-9171-9e0d8cec5235&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 021: Including type information in our syntax tree</title>
          <pubDate>Fri, 21 Mar 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/03/21/mini-rust-in-rust-021-typed-syntax-tree/</link>
          <guid>https://artificialworlds.net/blog/2025/03/21/mini-rust-in-rust-021-typed-syntax-tree/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/03/21/mini-rust-in-rust-021-typed-syntax-tree/">&lt;p&gt;We realised last time that our type checker needs to move on from just saying
&quot;yes&quot; or &quot;no&quot; for whether our program is OK: we need to keep hold of the type
information we work out, so we can use it for error messages and possibly even
within the evaluator.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 021: Including type information in our syntax tree&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2b9e013a-b944-43c8-89cb-94647d044ed1&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 020: Gaps in our type checker</title>
          <pubDate>Fri, 14 Mar 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/03/14/mini-rust-in-rust-020-gaps-in-type-check/</link>
          <guid>https://artificialworlds.net/blog/2025/03/14/mini-rust-in-rust-020-gaps-in-type-check/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/03/14/mini-rust-in-rust-020-gaps-in-type-check/">&lt;p&gt;The type checker is passing our unit tests, but it&#x27;s stopping our language from
actually working. Time to fill in the gaps.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 020: Gaps in our type checker&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d53469bf-a12b-4c0a-a48e-088beb61a5f4&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 019: Type checking an actual type error</title>
          <pubDate>Fri, 07 Mar 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/03/07/mini-rust-in-rust-019-type-check-assignment-sad/</link>
          <guid>https://artificialworlds.net/blog/2025/03/07/mini-rust-in-rust-019-type-check-assignment-sad/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/03/07/mini-rust-in-rust-019-type-check-assignment-sad/">&lt;p&gt;We have a plan for what error message we should get when we try to write code like &lt;code&gt;let x: i32 = 3.0;&lt;&#x2F;code&gt;. Let&#x27;s try to make it a reality!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 019: Type checking an actual type error&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;78c7033b-bd6f-488c-a6af-af65763d6bba&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 018: Type checking, the happy path</title>
          <pubDate>Fri, 28 Feb 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/02/28/mini-rust-in-rust-018-type-check-assignment-happy/</link>
          <guid>https://artificialworlds.net/blog/2025/02/28/mini-rust-in-rust-018-type-check-assignment-happy/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/02/28/mini-rust-in-rust-018-type-check-assignment-happy/">&lt;p&gt;This is it: we&#x27;re going to do some actual type-checking. We want to be able to identify that &lt;code&gt;let x: i32 = 3.0;&lt;&#x2F;code&gt; is wrong, but for now we&#x27;re going to check some things that are right. Type-checking this kind of thing is way easier than the full type inference we will eventually need, so it&#x27;s a gentle start.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 018: Type checking, the happy path&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;7fa9ad48-32c1-4961-b6d8-13bb454e253a&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 017: Handling float literals</title>
          <pubDate>Fri, 21 Feb 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/02/21/mini-rust-in-rust-017-lex-parse-eval-floats/</link>
          <guid>https://artificialworlds.net/blog/2025/02/21/mini-rust-in-rust-017-lex-parse-eval-floats/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/02/21/mini-rust-in-rust-017-lex-parse-eval-floats/">&lt;p&gt;We&#x27;re still on a mission to do some type-checking, and our plan is to allow
writing &lt;code&gt;let x: i32 = 3.0;&lt;&#x2F;code&gt;, then produce a type error because &lt;code&gt;3.0&lt;&#x2F;code&gt; is not an
integer that can be stored in an &lt;code&gt;i32&lt;&#x2F;code&gt;. The last piece of the puzzle is to be
able to handle the &lt;code&gt;3.0&lt;&#x2F;code&gt; bit - i.e. to be able to lex, parse and evaluate
floating-point numbers.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 017: Handling float literals&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;7911afb9-74c6-4b86-b067-85475279ae39&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 016: Tidying our parser</title>
          <pubDate>Fri, 14 Feb 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/02/14/mini-rust-in-rust-016-tidy-assignment-parsing/</link>
          <guid>https://artificialworlds.net/blog/2025/02/14/mini-rust-in-rust-016-tidy-assignment-parsing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/02/14/mini-rust-in-rust-016-tidy-assignment-parsing/">&lt;p&gt;We left our parser in an awful mess after the last video, so we&#x27;re going to tidy
it up a bit and make it easier to understand.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 016: Tidying our parser&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6fd56aff-1087-48d6-a39c-376403ab21f2&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 015: Parsing a typed assignment</title>
          <pubDate>Fri, 07 Feb 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/02/07/mini-rust-in-rust-015-parse-typed-assignment/</link>
          <guid>https://artificialworlds.net/blog/2025/02/07/mini-rust-in-rust-015-parse-typed-assignment/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/02/07/mini-rust-in-rust-015-parse-typed-assignment/">&lt;p&gt;We want to have a go at some type-checking, so the first step is to write some
code that causes a type error. We are going for &lt;code&gt;let x: i32 = 3.0;&lt;&#x2F;code&gt;. To do that,
we need to be able to lex and parse the &lt;code&gt;: i32&lt;&#x2F;code&gt; part...&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 015: Parsing a typed assignment&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;50beef82-cc52-45fe-aeac-e991573ccf13&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of
my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 014: Evaluating an assignment</title>
          <pubDate>Fri, 31 Jan 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/01/31/mini-rust-in-rust-014-evaluating-an-assignment/</link>
          <guid>https://artificialworlds.net/blog/2025/01/31/mini-rust-in-rust-014-evaluating-an-assignment/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/01/31/mini-rust-in-rust-014-evaluating-an-assignment/">&lt;p&gt;Finally, we are going to execute a real piece of code that actually looks like a
programming language! We are setting a variable to a value, and then using that
variable later on...&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 014: Evaluating an assigment&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;56c5ddcb-9261-455c-94d6-a2c81b1cc5a2&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 013: Fixing a bug in our lexer</title>
          <pubDate>Fri, 24 Jan 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/01/24/mini-rust-in-rust-013-fixing-a-bug-in-our-lexer/</link>
          <guid>https://artificialworlds.net/blog/2025/01/24/mini-rust-in-rust-013-fixing-a-bug-in-our-lexer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/01/24/mini-rust-in-rust-013-fixing-a-bug-in-our-lexer/">&lt;p&gt;We have been putting up with a weird problem with our lexer: up to now it
requires white space between each token. But, last time we created a
backtracking iterator that we can use to fix this problem, so let&#x27;s go ahead and
do it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 013: Fixing a bug in our lexer&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d3eb7ad2-dce2-4b60-9bd1-16f2b2132061&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 012: Parsing with backtracking</title>
          <pubDate>Fri, 17 Jan 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/01/17/mini-rust-in-rust-012-parsing-with-backtracking/</link>
          <guid>https://artificialworlds.net/blog/2025/01/17/mini-rust-in-rust-012-parsing-with-backtracking/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/01/17/mini-rust-in-rust-012-parsing-with-backtracking/">&lt;p&gt;We&#x27;ve built a toy parser, but it&#x27;s not going to last us. We need to make it a
bit more proper. Specifically, we need to be able to look ahead, and then
backtrack.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 012: Backtracking in our parser&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2cbc8cbf-80b3-4499-ae8b-afb9ad230812&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 011: Parsing an assignment plus expression</title>
          <pubDate>Fri, 10 Jan 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/01/10/mini-rust-in-rust-011-parsing-an-assignment-plus-expression/</link>
          <guid>https://artificialworlds.net/blog/2025/01/10/mini-rust-in-rust-011-parsing-an-assignment-plus-expression/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/01/10/mini-rust-in-rust-011-parsing-an-assignment-plus-expression/">&lt;p&gt;Last time we lexed an assignment statement. This time: parsing it, so we&#x27;re
ready to execute it!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 011: Parsing our first statement&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;07f0e1fa-ae25-438e-92ef-a654a4a18bf9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jezhiggins
    &lt;span class=&quot;comment_date&quot;&gt;2025-01-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have been waiting for you to peek!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2025-01-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Ha, well technically I am not going to peek :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 010: Lexing an assignment</title>
          <pubDate>Fri, 03 Jan 2025 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2025/01/03/mini-rust-in-rust-010-lexing-an-assignment/</link>
          <guid>https://artificialworlds.net/blog/2025/01/03/mini-rust-in-rust-010-lexing-an-assignment/</guid>
          <description xml:base="https://artificialworlds.net/blog/2025/01/03/mini-rust-in-rust-010-lexing-an-assignment/">&lt;p&gt;We&#x27;re ready to lex a proper statement: specifically a &quot;let&quot; statement that will
give a variable its value.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 010: Lexing a full statement&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;df68f11e-d62c-4a86-b296-cced6c5a39f5&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 009: Handling parsing errors</title>
          <pubDate>Fri, 27 Dec 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/12/27/mini-rust-in-rust-009-handling-parsing-errors/</link>
          <guid>https://artificialworlds.net/blog/2024/12/27/mini-rust-in-rust-009-handling-parsing-errors/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/12/27/mini-rust-in-rust-009-handling-parsing-errors/">&lt;p&gt;We promised ourselves we&#x27;d make sure error messages were good before it got too
tricky to do it right, so now we have to bite the bullet and make our parsing
errors nicer.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 009: Nicer parsing errors&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e4199547-c61d-4c63-87c1-f11d7bb67b39&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 008: Refactoring our parser</title>
          <pubDate>Fri, 20 Dec 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/12/20/mini-rust-in-rust-008-refactoring-our-parser/</link>
          <guid>https://artificialworlds.net/blog/2024/12/20/mini-rust-in-rust-008-refactoring-our-parser/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/12/20/mini-rust-in-rust-008-refactoring-our-parser/">&lt;p&gt;Our parser kinda works, but it needs some work to be less terrible. We&#x27;ve
realised we did a few things in awkward or wrong ways, so let&#x27;s fix them before
they get out of hand.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 008: Refactoring our parser&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;9ff34a59-9419-4099-8d80-1b0edd7fe583&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 007: Evaluating an operation</title>
          <pubDate>Fri, 13 Dec 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/12/13/mini-rust-in-rust-007-evaluating-an-operation/</link>
          <guid>https://artificialworlds.net/blog/2024/12/13/mini-rust-in-rust-007-evaluating-an-operation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/12/13/mini-rust-in-rust-007-evaluating-an-operation/">&lt;p&gt;Now that we can parse things, let&#x27;s evaluate them. By the end of this, we should
be able to use our little language as a mini-calculator (only for adding up so
far though).&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 007: Running some code!&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;7fad14d7-864f-4219-9b76-ce6b48b13642&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 006: Parsing an operation</title>
          <pubDate>Fri, 06 Dec 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/12/06/mini-rust-in-rust-006-parsing-an-operation/</link>
          <guid>https://artificialworlds.net/blog/2024/12/06/mini-rust-in-rust-006-parsing-an-operation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/12/06/mini-rust-in-rust-006-parsing-an-operation/">&lt;p&gt;We&#x27;re finally ready to parse things! Our lexer can do almost enough to allow us
to handle &quot;3 + 5&quot;, so we&#x27;ll make that ready, and then parse that expression into
a syntax tree.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 006: A tiny bit of parsing&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;cc98eb22-3075-465f-b921-0cff77774e21&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Message order in Matrix: right now, we are deliberately inconsistent</title>
          <pubDate>Wed, 04 Dec 2024 14:25:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/12/04/message-order-in-matrix/</link>
          <guid>https://artificialworlds.net/blog/2024/12/04/message-order-in-matrix/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/12/04/message-order-in-matrix/">&lt;p&gt;&lt;em&gt;After lots of conversations with Element colleagues about message order in
Matrix, and lots of surprises for me, I wanted to write down what I had learned
before I forgot, and also write down some principles I think we should try to
follow. A lot of this is just my half-formed opinions, and while I am very
grateful to everyone who helped educate me about all of this, it in no way
represents any kind of policy or consensus from Element or Matrix or anyone else
:-)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; I have attempted to sketch some possible solutions to this problem at
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;stitched-order&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;stitched-order&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-messages&quot;&gt;Finding messages&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re writing a Matrix client (e.g. a chat app), you need to ask the server
for messages that have been sent in a room. To do this, you need to download the
&quot;events&quot;, which are just messages plus other things you might need to know
about. Messages are one type of &quot;timeline&quot; event, meaning that they appear in
the main display area of a room, showing you what people said.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;via-sync&quot;&gt;Via &#x2F;sync&lt;&#x2F;h3&gt;
&lt;p&gt;The first and most common way to do this is to ask for the latest stuff that&#x27;s
happened, by hitting the
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#syncing&quot;&gt;&lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; API:&lt;&#x2F;p&gt;
&lt;pre&gt;
GET https:&#x2F;&#x2F;example.com&#x2F;_matrix&#x2F;client&#x2F;v3&#x2F;sync?since=s3333

{
    &quot;rooms&quot;: {&quot;join&quot;: {&quot;!roomid:example.com&quot;: {&quot;timeline&quot;: {
        &quot;events&quot;: [
            { &quot;content&quot;: {&quot;body&quot;: &quot;&lt;span style=&quot;color: green;&quot;&gt;How many roads?&lt;&#x2F;span&gt;&quot;, ... }, ... }
            { &quot;content&quot;: {&quot;body&quot;: &quot;&lt;span style=&quot;color: green;&quot;&gt;Forty-two.&lt;&#x2F;span&gt;&quot;, ... }, ... }
        ],
        &quot;prev_batch&quot;: &quot;s2222&quot;, ...
    }}}}, ...
}
&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;(Note: we&#x27;re not talking about
&quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#types-of-room-events&quot;&gt;state&lt;&#x2F;a&gt;&quot;
events here, which deal with e.g. who is a member of this room. Things get even
more interesting when you start thinking about them, because the order in which
they happen is critical to deciding who is banned, and similar issues.)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;via-messages-or-similar&quot;&gt;Via &#x2F;messages or similar&lt;&#x2F;h3&gt;
&lt;p&gt;The second way to get events is via one of the other APIs such as
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv3roomsroomidmessages&quot;&gt;&lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;,
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv3roomsroomidcontexteventid&quot;&gt;&lt;code&gt;&#x2F;context&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;,
or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv1roomsroomidrelationseventid&quot;&gt;&lt;code&gt;&#x2F;relations&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;
GET https:&#x2F;&#x2F;example.com&#x2F;_matrix&#x2F;client&#x2F;v3&#x2F;rooms&#x2F;!xyz%3Aexample.com&#x2F;messages?dir=b&amp;amp;from=s2222

{
    &quot;chunk&quot;: [
        { &quot;content&quot;: {&quot;body&quot;: &quot;&lt;span style=&quot;color: green;&quot;&gt;How many roads?&lt;&#x2F;span&gt;&quot;, ... }, ... }
        { &quot;content&quot;: {&quot;body&quot;: &quot;&lt;span style=&quot;color: green;&quot;&gt;Forty-two.&lt;&#x2F;span&gt;&quot;, ... }, ... }
    ],
    &quot;start&quot;: &quot;s2222&quot;,
    &quot;end&quot;: &quot;t1111&quot;, ...
}
&lt;&#x2F;pre&gt;
&lt;p&gt;This seems unremarkable at first glance: the &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; response even contains a
&lt;code&gt;prev_batch&lt;&#x2F;code&gt; token which we can use as the &lt;code&gt;from&lt;&#x2F;code&gt; query parameter to &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;
so we can page back through messages to find older ones.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-is-the-problem&quot;&gt;So what is the problem?&lt;&#x2F;h2&gt;
&lt;p&gt;These APIs return messages in a different order.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; API returns events in an order &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#syncing&quot;&gt;according to the arrival time of the
event on the homeserver&lt;&#x2F;a&gt;&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The spec for &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;
says it returns events &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv3roomsroomidmessages&quot;&gt;in chronological order. (The exact definition of
chronological is dependent on the server
implementation.)&lt;&#x2F;a&gt;&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;For &lt;code&gt;&#x2F;context&lt;&#x2F;code&gt; it also mentions chronological order.&lt;&#x2F;p&gt;
&lt;p&gt;For &lt;code&gt;&#x2F;relations&lt;&#x2F;code&gt; it contradicts itself, stating both &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv1roomsroomidrelationseventid&quot;&gt;events will be returned
in chronological
order&lt;&#x2F;a&gt;&quot;
(when talking about the &lt;code&gt;dir&lt;&#x2F;code&gt; parameter) and events will be &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#get_matrixclientv1roomsroomidrelationseventid&quot;&gt;ordered
topologically&lt;&#x2F;a&gt;&quot;
(in the &lt;code&gt;chunk&lt;&#x2F;code&gt; section). My guess is that the &lt;code&gt;dir&lt;&#x2F;code&gt; parameter docs were
erroneously copied from elsewhere, and topological ordering was intended.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Topological ordering&lt;&#x2F;strong&gt;: events in a Matrix room are stored in a mathematical
structure known as a directed acyclic graph. Topological ordering means using
this graph structure (which is independent of the timing of when messages
arrived on the server) to decide an order. This order is easy to calculate
consistently, but it can be illogical from a common-sense point of view.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Synapse, and (I think) other homeservers actually use topological order for
&lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;context&lt;&#x2F;code&gt; as well as &lt;code&gt;&#x2F;relations&lt;&#x2F;code&gt;. I am not convinced that this
actually complies with the spec, since topological order is very much not
chronological, by my understanding of the word&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#chrono&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-this-a-problem&quot;&gt;Why is this a problem?&lt;&#x2F;h2&gt;
&lt;p&gt;Imagine I have two Matrix clients, both logged in as me. I leave the first
client open, polling the &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; API and fetching events in order of their
arrival on the homeserver. I close the second client, and open it later. It will
run a &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;, but it will only receive the latest messages. If I scroll the
room upwards, it will fetch more messages using the &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt; API.&lt;&#x2F;p&gt;
&lt;p&gt;The two clients will show me the messages in a different order. Normally, the
orders are similar or identical, but if two homeservers were disconnected for a
while (a &quot;netsplit&quot; occurred) they can be very different.&lt;&#x2F;p&gt;
&lt;p&gt;Which order is correct? I would generally argue that the first client is most
likely to fit with your intuition (because messages that you saw later are
further down the screen), but it&#x27;s definitely arguable. In actual fact, when
messages were sent effectively in parallel, there is no &lt;em&gt;correct&lt;&#x2F;em&gt; order. What I
am hoping for is a &lt;em&gt;consistent&lt;&#x2F;em&gt; order, as far as possible.&lt;&#x2F;p&gt;
&lt;p&gt;I would strongly argue that these two clients should show messages (and other
events) in the same order.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I do feel honour-bound at this point to say that I spoke to a colleague
recently who disagreed with this principle, and said that because of the
different usage of these two clients, it was OK, or even useful, that they
showed different results. I definitely disagree, but it&#x27;s worth pointing out
that this is a debatable point.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It&#x27;s also worth saying that even a lone client can exhibit this inconsistency,
if it doesn&#x27;t store all messages forever. If it deletes some messages to save
storage space, when the user pages up to read those messages, they will be
fetched using &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;, so will appear in a different order from what the
user saw originally when they were fetched via &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;. There is currently no
API that can re-fetch messages in the same order they were first received over
&lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-big-a-problem-is-this&quot;&gt;How big a problem is this?&lt;&#x2F;h2&gt;
&lt;p&gt;Does it really matter if a few messages are in a different order? On the face of
it, maybe not. In most cases, the differences are minor, and when they are more
significant this is the result of a significant problem like a netsplit or
malicious behaviour.&lt;&#x2F;p&gt;
&lt;p&gt;I will admit that, even though I said I was not talking about state events (the
important events that define e.g. who is a member of the room), part of my
motivation here is that I want the order of state events to be consistent,
because there are times when a user really wants to examine the history of what
happened&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#history&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, and doesn&#x27;t want that to change under them.&lt;&#x2F;p&gt;
&lt;p&gt;However, I personally think that even if we ignore state events, we should do
our level best to order messages consistently. How should a user interpret a
change in order? What does it mean to them? Most likely, if they notice, they
will figure that Matrix is just a bit flaky. In the worst case, we might
&quot;gaslight&quot; them: they remember things happening in a particular order, but
when they check back they find that the evidence contradicts them.&lt;&#x2F;p&gt;
&lt;p&gt;If we accept that clients should display a linear view of what happened (despite
the fact that in reality things may have happened in parallel) then I think we
should work hard to make that view consistent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-fix-it&quot;&gt;How to fix it?&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;use-topological-order-everywhere&quot;&gt;Use topological order everywhere?&lt;&#x2F;h3&gt;
&lt;p&gt;One way that a client could &quot;fix&quot; this problem without any spec changes at all
would be to ignore &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; timeline responses completely, and repeatedly call
&lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt; to get messages to display. This would ensure that messages are
displayed in a consistent order, but it has several critical disadvantages.&lt;&#x2F;p&gt;
&lt;p&gt;Firstly, this is clearly is not the intention of the spec authors, and is
inefficient since it involves throwing away information that the server worked
to produce.&lt;&#x2F;p&gt;
&lt;p&gt;Secondly, assuming that messages appear in topological order, if some old
messages arrive late (e.g. due to a netsplit), this will mean that messages
appear &quot;in the past&quot;, high up the timeline, even though the user has not read
them. To make this happen, the client would need to keep repeating old calls to
&lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;, to check whether the past has changed, and the client would need to
find a way to display these late-arriving changes to the user.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;persistent-sync-order&quot;&gt;Persistent sync order&lt;&#x2F;h3&gt;
&lt;p&gt;I believe that the order messages arrive from &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; is the correct order for a
client: as soon as the homeserver has a message, it should hand it over to the
client, and the client should show that it arrived by rendering it at the bottom
of the timeline.&lt;&#x2F;p&gt;
&lt;p&gt;I also believe that all of my clients should see the same order of messages.&lt;&#x2F;p&gt;
&lt;p&gt;So the logical conclusion is that the homeserver should be able to provide a
back-paginatable view of messages in the order they were provided via &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;
and by extension, if no client happened to be syncing at the time, the order in
which they &lt;em&gt;would have&lt;&#x2F;em&gt; been provided, which is essentially the order in which
they arrived at the homeserver.&lt;&#x2F;p&gt;
&lt;p&gt;One way to implement this would be to change the &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt; and other APIs to
return messages in this order. In the case of the &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;context&lt;&#x2F;code&gt;
APIs, I even think this would comply with the spec as it is now. One possible
implementation for homeservers would be to mark each message with a timestamp
when they first saw it, and sort their responses based on this timestamp. Spec
issue &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec&#x2F;issues&#x2F;852&quot;&gt;#852&lt;&#x2F;a&gt; actually
proposes this change for &lt;code&gt;&#x2F;messages&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: this might make it difficult for homeservers that process incoming
events in parallel, requiring some kind of synchronisation mechanism to assign
timestamps, or some other mechanism to provide a consistent order. The exact
order of events that arrive very close to each other is not important though,
so long as the order is consistent.&lt;&#x2F;p&gt;
&lt;p&gt;It is worth noting that whenever a client is syncing, the homeserver already
chooses an order for the events it provides over &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt;, proving that a
linear order is possible in principle.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;An alternative is to continue providing events in any order, but add some kind
of order number that allows clients to sort events into &lt;code&gt;&#x2F;sync&lt;&#x2F;code&gt; order.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;pull&#x2F;4033&quot;&gt;MSC4033&lt;&#x2F;a&gt;
proposes this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;of-course-it-s-much-worse-than-this&quot;&gt;Of course, it&#x27;s much worse than this&lt;&#x2F;h2&gt;
&lt;p&gt;Feel free to stop reading here. I&#x27;ve made my main point.&lt;&#x2F;p&gt;
&lt;p&gt;But if you want to know how difficult this problem &lt;em&gt;really&lt;&#x2F;em&gt; is, read on!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;state-resolution-can-change-the-past&quot;&gt;State resolution can change the past&lt;&#x2F;h3&gt;
&lt;p&gt;So far, we&#x27;ve been assuming that if the homeserver just passes on messages to
the client as soon as it receives them, this will result in a reasonably
sensible order and an accurate reflection of the timeline. In fact, this is not
really true, because whether we like it or not, history as we understand it can
change.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, when the homeserver performs &quot;state resolution&quot; (an evaluation of
which state events are considered valid based on who is a member of the room and
what permissions they have), some events can be effectively removed because it
turns out the person who created them didn&#x27;t have permission to do so. Because
Matrix is distributed, it definitely does happen that a homeserver evaluates an
event as valid at one point (and passes it on to clients) and then later has to
change its mind and decide it is not valid&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#valid&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Currently, we don&#x27;t have a way of telling clients that messages should be
removed because of a change like this. (For state events, there are mechanisms
to tell clients they need to update their state, but not actually to delete
state events, as I understand it.)&lt;&#x2F;p&gt;
&lt;p&gt;I think we need to allow homeservers to send &quot;deny&quot; items to clients, which tell
a client to delete events, to cover this case. (Note: I don&#x27;t use the word
&quot;event&quot; here since these &lt;em&gt;items&lt;&#x2F;em&gt; would by necessity be created by the homeserver, not a
client, and would not be signed by a client device.)&lt;&#x2F;p&gt;
&lt;p&gt;I also think these deny items should be part of the linear history of a room,
as opposed to being signals to edit that history retrospectively. This way,
clients can show clearly the history of what they displayed to the user, and why
it changed when it did. The alternative is to &quot;lie&quot; to the user, &quot;pretending&quot;
that these events never existed, when the user actually saw them. How the client
presents this to the user would certainly need to be explored. For example, the
events might disappear from the timeline but some kind of &quot;detailed history&quot;
view might show that they used to exist and were later denied.&lt;&#x2F;p&gt;
&lt;p&gt;I think it&#x27;s really important that we show the user what really happened &lt;em&gt;from
their perspective&lt;&#x2F;em&gt; (in a persistent form). Anything else is confusing and
betrays users&#x27; trust.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when-the-homeserver-backpaginates&quot;&gt;When the homeserver backpaginates&lt;&#x2F;h3&gt;
&lt;p&gt;So far, we&#x27;ve been assuming that the homeserver has access to all the events,
but that is not always true. If a client asks for some events that a homeserver
does not have, the homeserver can ask another homeserver for them.&lt;&#x2F;p&gt;
&lt;p&gt;Now, when these new, old events arrive, they are very strange: they are new to
this homeserver, but we only had to fetch them from the other server because
they are old! Clients don&#x27;t want to display them at the bottom of the timeline,
because they were only requested when a user scrolled a long way up to the
top of the timeline.&lt;&#x2F;p&gt;
&lt;p&gt;So these new, old events need to be inserted further back in the linear history
that we are building, not at the end.&lt;&#x2F;p&gt;
&lt;p&gt;This is a difficult problem because we need to figure out where they need
inserting - it may not be at the start because we may have multiple gaps.&lt;&#x2F;p&gt;
&lt;p&gt;However, I still argue that we should solve this difficult problem &lt;em&gt;on the
server&lt;&#x2F;em&gt;, and present it as straightforward to the client i.e. the server should
respond to a request for these events by returning them as if they already
existed in the linear timeline, and from that moment on always returning them at
the same point when asked.&lt;&#x2F;p&gt;
&lt;p&gt;So from a client&#x27;s point of view, these events should be indistiguishable from
events that were on the server already.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;different-people-just-are-going-to-see-things-in-different-orders&quot;&gt;Different people just are going to see things in different orders&lt;&#x2F;h3&gt;
&lt;p&gt;It would be nice if everyone had the same view. But from different people&#x27;s
point of view, things genuinely happened in different orders. If I typed and
sent my message while someone else&#x27;s message was travelling to me over the
Internet, I will think that my message was first, and they will think theirs was
first.&lt;&#x2F;p&gt;
&lt;p&gt;With two users on the same homeserver, we could choose to make the homeserver
the arbiter, picking one message to come first. (Note: we &lt;em&gt;don&#x27;t&lt;&#x2F;em&gt; currently do
this, but we could.)&lt;&#x2F;p&gt;
&lt;p&gt;But, when two users are on different homeservers, this problem is unsolvable.
An important part of the design of Matrix is that two homeservers can disagree
on the exact order of messages, and still interoperate with each other. This is
what the long words are for: &quot;directed acyclic graph&quot;, &quot;eventual consistency&quot;
and &quot;state resolution&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;So we can never give everyone a consistent view of the order of messages.&lt;&#x2F;p&gt;
&lt;p&gt;In this article I am arguing that a single person should always get a consistent
order when they come back to a room or look via a different client.&lt;&#x2F;p&gt;
&lt;p&gt;I also think I would like to argue that users on the same homeserver should see a
view consistent with each other, but I have not developed that argument here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;addendum-receipts&quot;&gt;Addendum: receipts&lt;&#x2F;h2&gt;
&lt;p&gt;The spec for read receipts states that a receipt means that
&quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;spec.matrix.org&#x2F;v1.12&#x2F;client-server-api&#x2F;#receipts&quot;&gt;the user has read up to a given event&lt;&#x2F;a&gt;&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;In order to understand this, it is, of course, critically important to know
which events are before or after this event i.e. what their order is. In
practice, when existing homeservers report the read status of a room they use
the order in which they received the message as the order for receipts, which I
believe is a good order for this purpose.&lt;&#x2F;p&gt;
&lt;p&gt;As it stands, for an arbitrary event, the spec does not provide a way for a
client to determine which events are before or after it in this ordering, making
it essentially impossible for clients to handle read receipts in a fully-correct
way, or to resolve receipts consistently with the homeserver. The information is
hidden from the client! See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.google.com&#x2F;presentation&#x2F;d&#x2F;1H1gxRmRFAm8d71hCILWmpOYezsvdlb7cB6ANl-20Gns&#x2F;edit?usp=sharing&quot;&gt;Deciding whether a room or thread is
unread&lt;&#x2F;a&gt;
for more detail.&lt;&#x2F;p&gt;
&lt;p&gt;Ordering events in server-arrival order would improve this situation, but to
make it easy for client authors to get receipts right, I believe we need an
order number for each event, making it easy to compare any two events&#x27; order,
without constructing a timeline and placing them on it. This is
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;pull&#x2F;4033&quot;&gt;MSC4033&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;addendum-linear-timeline&quot;&gt;Addendum: linear timeline&lt;&#x2F;h2&gt;
&lt;p&gt;After I wrote the first draft of this post, some colleagues and I discussed it,
and thought up some reasons why a &quot;persistent sync order&quot; or &quot;linearised
timeline&quot; has problems we would need to solve.&lt;&#x2F;p&gt;
&lt;p&gt;The problems come when the homeserver has a gap in its timeline, and then uses
backpagination (or &quot;backfilling&quot;) to fetch events within that gap.&lt;&#x2F;p&gt;
&lt;p&gt;Quoting Rich vdh&#x27;s summary of our conversation:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Suppose Homeserver A has been participating in a busy room for a long time (10 years, for argument&#x27;s sake).&lt;&#x2F;p&gt;
&lt;p&gt;A netsplit happens; during the netsplit, homeserver B sends 200 messages.
Eventually, the netsplit heals. Homeserver A ends up receiving some, but
definitely not all, of those 200 messages. We now have a &quot;gap&quot; in the DAG.&lt;&#x2F;p&gt;
&lt;p&gt;Time passes, and another 50 messages or so get sent.&lt;&#x2F;p&gt;
&lt;p&gt;Now, a user comes online. First of all, they only see the recent 50 messages.
But they scroll up, and get to the point of the &quot;gap&quot;. Assuming we backfill
homeserver B&#x27;s messages, where do they fit in the timeline?&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;at the beginning (ie, 10 years ago)?&lt;&#x2F;li&gt;
&lt;li&gt;at the end (ie, the user doesn&#x27;t actually see the messages until they scroll back down to &quot;now&quot;)&lt;&#x2F;li&gt;
&lt;li&gt;or do we try and slot them in at the right point in the timeline?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I think the only plausible answer is 3, but it means that you could scroll
past the same point in the timeline twice and see different messages (because
we might not have been able to backfill the message the first time the user
scrolled past).&lt;&#x2F;p&gt;
&lt;p&gt;.... Which I think really means that we have to say to the client &quot;&amp;lt;some
messages missing here&amp;gt;&quot; and the client needs to reflect that in the UI.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;thanks&quot;&gt;Thanks&lt;&#x2F;h2&gt;
&lt;p&gt;Thank you to Erik J, Rich vdH, Kegan D, Florian H and others for discussions
leading up to this article, and for help with writing it. All mistakes,
misunderstandings and naiveties are my own.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;&#x2F;h2&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;chrono&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;The ambiguity of the term &quot;chronological&quot; was &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;pull&#x2F;2069#discussion_r290342863&quot;&gt;raised in
2019&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;history&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;On &quot;the history of what happened&quot;, since there is no definitive
order of events in general, it is impossible to provide a linear history
that always agrees with the outcome chosen by state resolution. What I am
looking for is that we should provide a linear history that does not change
for the user when they check back on it later.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;valid&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Note: technically, no event is &quot;invalid&quot; in principle - it simply
belongs in a particular place in the graph. When we say an event has become
invalid, we really mean that the event is not included in what the server
considers the &lt;em&gt;current state&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2024-12-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hackernews commentary is at &lt;a href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=42324114&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=42324114&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    donpdonp
    &lt;span class=&quot;comment_date&quot;&gt;2024-12-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great writeup! I believe the authors meant for clients to always display the topological ordering.&lt;br&#x2F;&gt;
&quot;…messages appear &quot;in the past&quot;, high up the timeline, even though the user has not read them. To make this happen, the client would need to keep repeating old calls to &#x2F;messages,&quot;&lt;&#x2F;p&gt;
&lt;p&gt;This is why &#x2F;sync returns activity in time-received order, so the client can apply these changes to the topology. &#x2F;sync was never meant to be a displayed timeline, but an efficient way to get state changes without searching into history.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 005: Making our lexer an iterator</title>
          <pubDate>Fri, 29 Nov 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/11/29/mini-rust-in-rust-005-making-our-lexer-an-iterator/</link>
          <guid>https://artificialworlds.net/blog/2024/11/29/mini-rust-in-rust-005-making-our-lexer-an-iterator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/11/29/mini-rust-in-rust-005-making-our-lexer-an-iterator/">&lt;p&gt;Our lex function returns an iterator, but in the implementation we were faking
it. Let&#x27;s do it properly.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 005: Our lexer should be an iterator&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;8ccaef25-fa25-4314-8d39-46ab4dd39b3b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 004: Refactoring our lexer</title>
          <pubDate>Fri, 22 Nov 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/11/22/mini-rust-in-rust-004-refactoring-our-lexer/</link>
          <guid>https://artificialworlds.net/blog/2024/11/22/mini-rust-in-rust-004-refactoring-our-lexer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/11/22/mini-rust-in-rust-004-refactoring-our-lexer/">&lt;p&gt;Refactoring our lexer, so all the awful things we did to make the tests pass are
behind us.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 004: Refactoring our lexer&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c15ce6a7-b800-4e7f-bc1c-d75d5a2431d8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jezhiggins
    &lt;span class=&quot;comment_date&quot;&gt;2024-11-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I am so happy you put the counting iterator around Chars ❤️&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2024-11-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I live to serve :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 003: Much prettier errors</title>
          <pubDate>Fri, 15 Nov 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/11/15/mini-rust-in-rust-003-much-prettier-errors/</link>
          <guid>https://artificialworlds.net/blog/2024/11/15/mini-rust-in-rust-003-much-prettier-errors/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/11/15/mini-rust-in-rust-003-much-prettier-errors/">&lt;p&gt;More of the very basic structure of our interpreter: making the error messages from the lexer point at the exact line of code, in a pretty way.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 003: Much prettier errors&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;835f01a4-c74d-4e96-8cb6-ee888537b138&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 002: Errors while lexing</title>
          <pubDate>Fri, 08 Nov 2024 12:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/11/08/mini-rust-in-rust-002-errors-while-lexing/</link>
          <guid>https://artificialworlds.net/blog/2024/11/08/mini-rust-in-rust-002-errors-while-lexing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/11/08/mini-rust-in-rust-002-errors-while-lexing/">&lt;p&gt;Continuing writing our mini-rust in Rust. Figuring out how to throw an error when lexing goes wrong.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 002: Errors while lexing&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;078d563a-765f-4720-a14e-2589ac54a867&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mini-rust in Rust 001: Lexing an int</title>
          <pubDate>Thu, 31 Oct 2024 10:05:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/31/mini-rust-in-rust-001-lexing-an-int/</link>
          <guid>https://artificialworlds.net/blog/2024/10/31/mini-rust-in-rust-001-lexing-an-int/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/31/mini-rust-in-rust-001-lexing-an-int/">&lt;p&gt;Introducing my new series: writing a little Rust-like language in Rust. It will be an interpreted language, and (for now at least) it will try to work as much like real Rust as possible. Maybe one day it will be a useful language, but for now it&#x27;s purely for fun. (You can imagine it being a nice scripting language, or an embedded language inside Rust projects, but for that it might need to deviate from Rust - maybe avoiding ownership and references and using a Python-like runtime model.)&lt;&#x2F;p&gt;
&lt;p&gt;In this video we write enough code to understand that an integer is made up of several digits next to each other, and we wrap that up in a &quot;token&quot;. This is the start of a Lexer.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Mini-rust in Rust 001: Lexing an int&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;020816ed-a00c-426d-bbfd-9fd93d74faa8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can find the source code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;milk&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;milk&lt;&#x2F;a&gt; and more of my stuff at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 53: Exercises for module G (q4)</title>
          <pubDate>Wed, 23 Oct 2024 20:00:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/23/rust-101-53-exercises-for-module-g-q4/</link>
          <guid>https://artificialworlds.net/blog/2024/10/23/rust-101-53-exercises-for-module-g-q4/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/23/rust-101-53-exercises-for-module-g-q4/">&lt;p&gt;Creating a custom Python extension in Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 53: Exercises for module G (q4)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e896b742-5708-4a62-888f-1137636a16ec&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;FFI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (FFI): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;49: FFI&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-50-exercises-for-module-g-q1&quot;&gt;50: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-51-exercises-for-module-g-q2&quot;&gt;51: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-52-exercises-for-module-g-q3&quot;&gt;52: Exercise 3&lt;&#x2F;a&gt;, &lt;strong&gt;53: Exercise 4&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;G-ffi&#x2F;&quot;&gt;Rust 101 - module G&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 52: Exercises for module G (q3)</title>
          <pubDate>Wed, 23 Oct 2024 19:56:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/23/rust-101-52-exercises-for-module-g-q3/</link>
          <guid>https://artificialworlds.net/blog/2024/10/23/rust-101-52-exercises-for-module-g-q3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/23/rust-101-52-exercises-for-module-g-q3/">&lt;p&gt;Using cargo-bindgen to generate bindings, automating what we need to do to use existing C code inside a Rust project.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 52: Exercises for module G (q3)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;f0ccd293-6199-4b8c-ba0f-21132c5180f7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;FFI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (FFI): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;49: FFI&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-50-exercises-for-module-g-q1&quot;&gt;50: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-51-exercises-for-module-g-q2&quot;&gt;51: Exercise 2&lt;&#x2F;a&gt;, &lt;strong&gt;52: Exercise 3&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-53-exercises-for-module-g-q4&quot;&gt;53: Exercise 4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;G-ffi&#x2F;&quot;&gt;Rust 101 - module G&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 51: Exercises for module G (q2)</title>
          <pubDate>Wed, 23 Oct 2024 19:52:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/23/rust-101-51-exercises-for-module-g-q2/</link>
          <guid>https://artificialworlds.net/blog/2024/10/23/rust-101-51-exercises-for-module-g-q2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/23/rust-101-51-exercises-for-module-g-q2/">&lt;p&gt;Trying out calling Rust code from with a C program (should help with calling Rust from any compiled-to-native language too).&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 51: Exercises for module G (q2)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;70572d3c-74f4-446f-bbf5-d6b6f75eaf47&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;FFI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (FFI): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;49: FFI&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-50-exercises-for-module-g-q1&quot;&gt;50: Exercise 1&lt;&#x2F;a&gt;, &lt;strong&gt;51: Exercise 2&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-52-exercises-for-module-g-q3&quot;&gt;52: Exercise 3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-53-exercises-for-module-g-q4&quot;&gt;53: Exercise 4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;G-ffi&#x2F;&quot;&gt;Rust 101 - module G&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 50: Exercises for module G (q1)</title>
          <pubDate>Wed, 23 Oct 2024 19:44:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/23/rust-101-50-exercises-for-module-g-q1/</link>
          <guid>https://artificialworlds.net/blog/2024/10/23/rust-101-50-exercises-for-module-g-q1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/23/rust-101-50-exercises-for-module-g-q1/">&lt;p&gt;Calling C code from Rust, Rust code from C-family languages, using cargo-bindgen, and creating Python extensions.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 50: Exercises for module G (q1)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;53b8ef28-6435-4f05-bc1a-a5bcd52001e7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;FFI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (FFI): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;49: FFI&lt;&#x2F;a&gt;, &lt;strong&gt;50: Exercise 1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-51-exercises-for-module-g-q2&quot;&gt;51: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-52-exercises-for-module-g-q3&quot;&gt;52: Exercise 3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-53-exercises-for-module-g-q4&quot;&gt;53: Exercise 4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;G-ffi&#x2F;&quot;&gt;Rust 101 - module G&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 49: Foreign function interfaces (interacting with other languages)</title>
          <pubDate>Wed, 23 Oct 2024 19:34:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/23/rust-101-49-foreign-function-interfaces/</link>
          <guid>https://artificialworlds.net/blog/2024/10/23/rust-101-49-foreign-function-interfaces/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/23/rust-101-49-foreign-function-interfaces/">&lt;p&gt;Calling C code from Rust, Rust code from C-family languages, using cargo-bindgen, and creating Python extensions.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 49: Foreign function interfaces (interacting with other languages)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;01bc1703-a110-4a10-9231-c81a4730bad7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;FFI&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (FFI): &lt;strong&gt;49: FFI&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-50-exercises-for-module-g-q1&quot;&gt;50: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-51-exercises-for-module-g-q2&quot;&gt;51: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-52-exercises-for-module-g-q3&quot;&gt;52: Exercise 3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-53-exercises-for-module-g-q4&quot;&gt;53: Exercise 4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;G-ffi&#x2F;&quot;&gt;Rust 101 - module G&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;G-ffi&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>19 years of blogging</title>
          <pubDate>Tue, 22 Oct 2024 08:33:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/22/19-years-of-blogging/</link>
          <guid>https://artificialworlds.net/blog/2024/10/22/19-years-of-blogging/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/22/19-years-of-blogging/">&lt;p&gt;As you may have noticed, I recently upgraded my blog from WordPress to
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&quot;&gt;Zola&lt;&#x2F;a&gt;. This means that my home computer does the work
of rendering the pages to HTML once, and my web server only has to provide the
rendered HTML to your browser. This should mean the pages appear much faster on
your screen, and probably less energy is used in the process. I&#x27;ve also taken
the opportunity to make the pages less cluttered and more focussed on the
article content.&lt;&#x2F;p&gt;
&lt;p&gt;So if you&#x27;ll allow me a little self-indulgence, I&#x27;d like to look back over the
history of this blog.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technology-changes-over-time&quot;&gt;Technology changes over time&lt;&#x2F;h2&gt;
&lt;p&gt;I started blogging in 2005, wanting to talk about more free&#x2F;open source work,
especially my TV guide &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;freeguide&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt;
(still going strong). My approach was RSS-first: I hand-wrote the RSS XML files,
and wrote some simple Python code to generate an HTML web site from the RSS, and
then I uploaded all of it to the free web space provided to my by my Internet
Service Provider.&lt;&#x2F;p&gt;
&lt;p&gt;In 2007 I moved to WordPress because I wanted to be able to write blog posts
while on the move, and it allowed me to type them in to a web form even if I was
on the train or at work. I stuck with WordPress for a long time - it gave me the
flexibility I needed, and I didn&#x27;t want to break existing URLs.&lt;&#x2F;p&gt;
&lt;p&gt;As the blog became more popular, I made gradual changes to try to speed up its
performance - I upgraded to a virtual server from shared web space, and later
paid more for a more powerful server. I installed various caching plugins,
working to reduce the inherent overhead of a PHP-based system like WordPress
that constructs your pages on-the-fly whenever they are requested.&lt;&#x2F;p&gt;
&lt;p&gt;The next significant technology change was 16 years later, when I installed a
WordPress plugin to allow directly following the blog via the fediverse.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;statistics&quot;&gt;Statistics&lt;&#x2F;h2&gt;
&lt;p&gt;Some stats for you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;19 years of blogging so far&lt;&#x2F;li&gt;
&lt;li&gt;517 blog posts&lt;&#x2F;li&gt;
&lt;li&gt;140 images and videos&lt;&#x2F;li&gt;
&lt;li&gt;150 people following comment threads via email (sorry, lost in the transition,
but you can re-sign up on the new system based on
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;remark42.com&#x2F;&quot;&gt;Remark42&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;4 people following via the fediverse (sorry, I lost this in the transition!
Please follow @andybalaam@mastodon.social)&lt;&#x2F;li&gt;
&lt;li&gt;655 comments&lt;&#x2F;li&gt;
&lt;li&gt;176,945 spam comments blocked, according to Akismet&lt;&#x2F;li&gt;
&lt;li&gt;7,637 IPs blocked by Limit Login Attempts plugin&lt;&#x2F;li&gt;
&lt;li&gt;378K total visitors since I switched to Koto analytics in 2020&lt;&#x2F;li&gt;
&lt;li&gt;Peak traffic: ~15K views&#x2F;month (2016-2020, when my YouTube videos were getting
more views)&lt;&#x2F;li&gt;
&lt;li&gt;Current traffic: ~3K views&#x2F;month&lt;&#x2F;li&gt;
&lt;li&gt;Time to build in Zola: 13s&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;blog-views.png&quot; alt=&quot;Views over time for this blog: starts high in 2020, and drops off, stabilising at about 2K views&#x2F;month&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;highlights&quot;&gt;Highlights&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Most popular post: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10&#x2F;17&#x2F;bash-associative-array-examples&#x2F;&quot;&gt;Bash associative array
examples&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;108,892 views since 2020&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Most useful post: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;Making 100 million requests with Python
aiohttp&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;15,831 views since 2020&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Weird that it&#x27;s popular: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;02&#x2F;23&#x2F;how-to-analyse-a-phd-heap-dump-from-an-ibm-jvm&#x2F;&quot;&gt;How to analyse a .phd heap dump from an IBM
JVM&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;14,830 views since 2020&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Best post series: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;09&#x2F;24&#x2F;how-to-write-a-programming-language-part-1-the-lexer&#x2F;&quot;&gt;How to write a programming
language&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;91 views since 2020&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;the-transition&quot;&gt;The transition&lt;&#x2F;h2&gt;
&lt;p&gt;I was determined to keep as many URLs working as possible, and one of the
attractions of Zola was the fact that I could control the post URLs exactly as I
wanted.&lt;&#x2F;p&gt;
&lt;p&gt;I did need to write some code to make it happen.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some of the PRs that got me here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Jieiku&#x2F;abridge&#x2F;pull&#x2F;210&quot;&gt;github.com&#x2F;Jieiku&#x2F;abridge&#x2F;pull&#x2F;210&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;3&quot;&gt;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;4&quot;&gt;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;4&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;5&quot;&gt;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;5&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;6&quot;&gt;github.com&#x2F;TatriX&#x2F;wordpress-to-zola&#x2F;pull&#x2F;6&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;Kanedias&#x2F;html2md&#x2F;-&#x2F;merge_requests&#x2F;5&quot;&gt;gitlab.com&#x2F;Kanedias&#x2F;html2md&#x2F;-&#x2F;merge_requests&#x2F;5&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;with quite a few still to come.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enjoy-the-next-19-years&quot;&gt;Enjoy the next 19 years&lt;&#x2F;h2&gt;
&lt;p&gt;I hope the new format is pleasing to you. I&#x27;m fairly sure the pages will load
quicker, which should make life a tiny bit nicer when you land on one of my
pages.&lt;&#x2F;p&gt;
&lt;p&gt;Do get in touch and let me know what you think! You can leave a comment below,
ping me on Mastodon
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt; or join my
Matrix room
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#andybalaam:matrix.org&#x2F;&quot;&gt;#andybalaam:matrix.org&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Or, exactly like any time in the last 19 years, just follow the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;rss.xml&quot;&gt;RSS
feed&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Deployment is important</title>
          <pubDate>Mon, 21 Oct 2024 02:23:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/21/2024-10-21-deployment-is-important/</link>
          <guid>https://artificialworlds.net/blog/2024/10/21/2024-10-21-deployment-is-important/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/21/2024-10-21-deployment-is-important/">&lt;p&gt;I just evaluated and installed software for providing comments and statistics
for this blog. Here are my experiences of the deployment process:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A Python comment system. Uses one of the 17 outdated installer&#x2F;packaging
systems for Python (none of which works). Read a lot of docs and made sure I
had a virtual env before I installed anything. &lt;code&gt;pip install&lt;&#x2F;code&gt; fails to compile
one of its C dependencies on the target host because I don&#x27;t have the Python
development libraries installed and can&#x27;t install packages. Tried a lot of
different things to get it to work, including pyinstaller. Really wanted this
to work. It didn&#x27;t work, and I gave up.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A node statistics system. Requires node, which clashed with the
already-installed outdated node, until I finally figured this out and put the
path to the node I downloaded into PATH, earlier than everything else.
Requires &lt;code&gt;yarn&lt;&#x2F;code&gt; but I was able to &lt;code&gt;npm install&lt;&#x2F;code&gt; that no problem. Builds
a massive amount of stuff on your deployment host, including compiling various
C dependencies, and building some map data of some kind. Totally killed my
VPS, taking my web site down. The next day my web hosting company killed all
processes on my VPS, presumably due to excess memory usage. Finally got it
working by building it locally and copying the built artifacts across. Is
there some better way? Anyway, it&#x27;s working now.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A Go comments system. You just download a single EXE and run it. It worked
immediately.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Deployment is important. There used to be a time when PHP beat all other comers
hands-down because everyone used outdated web hosts (like I still do) who
provided an Apache already set up for PHP. These days, languages that compile to
an EXE are streets ahead.&lt;&#x2F;p&gt;
&lt;p&gt;Side note: of course I would have used Rust programs for both of these jobs if I
could find mature ones that fit my needs, but I couldn&#x27;t. The Rust deployment
story is almost as good as Go&#x27;s. (The &quot;almost&quot; is because Go programs tend not
to depend on the C standard library, so there are even fewer things to go
wrong.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 48: Exercises for module F (q3)</title>
          <pubDate>Mon, 21 Oct 2024 01:22:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/21/rust-101-48-exercises-for-module-f-q3/</link>
          <guid>https://artificialworlds.net/blog/2024/10/21/rust-101-48-exercises-for-module-f-q3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/21/rust-101-48-exercises-for-module-f-q3/">&lt;p&gt;Implementing our own Result type that has a specific memory layout, so it can be used by Roc code.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 48: Exercises for module F (q3)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d5d7cafd-b08d-4814-8fe9-2b6bd5109283&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;strong&gt;48: Exercise 3&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 47: Exercises for module F (q2)</title>
          <pubDate>Mon, 21 Oct 2024 00:23:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/21/rust-101-47-exercises-for-module-f-q2/</link>
          <guid>https://artificialworlds.net/blog/2024/10/21/rust-101-47-exercises-for-module-f-q2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/21/rust-101-47-exercises-for-module-f-q2/">&lt;p&gt;Wrapping a C function with safe Rust code.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 47: Exercises for module F (q2)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a6582570-fcd7-4492-bd38-e4730b0dc6e7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;strong&gt;47: Exercise 2&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-48-exercises-for-module-f-q3&quot;&gt;48: Exercise 3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Heads-up: moving away from WordPress</title>
          <pubDate>Wed, 16 Oct 2024 22:51:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/16/heads-up-moving-away-from-wordpress/</link>
          <guid>https://artificialworlds.net/blog/2024/10/16/heads-up-moving-away-from-wordpress/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/16/heads-up-moving-away-from-wordpress/">&lt;p&gt;This is an early warning: I am actively working to move this blog away from WordPress, to a static site created using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am planning to keep as many URLs working as humanly possible, and the RSS feed URL should be unchanged. (With any luck, the permalinks inside will also be unchanged, but I won&#x27;t be preserving the GUIDs, so it might pollute your feed: sorry, but on past form this should only happen about every 16 years.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;BUT&lt;&#x2F;strong&gt; this means you will no longer be able to follow this blog via ActivityPub. I&#x27;m really sorry to those people doing that. I recommend following me on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt; - I almost always toot every blog post.&lt;&#x2F;p&gt;
&lt;p&gt;The good news is that this is going to make the blog way faster to use, easier to read, and reduce its energy usage.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2024-10-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;And this is no longer WordPress. Let me know how it works. I’ll be writing a new post soon to explain what I did to create this new site.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2024-10-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Just in case, the old blog is at &lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;old&#x2F;blog&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;artificialworlds.net&#x2F;old&#x2F;blog&lt;&#x2F;a&gt; . For a while…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 46: Exercises for module F (q1)</title>
          <pubDate>Mon, 07 Oct 2024 18:58:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/10/07/rust-101-46-exercises-for-module-f-q1/</link>
          <guid>https://artificialworlds.net/blog/2024/10/07/rust-101-46-exercises-for-module-f-q1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/10/07/rust-101-46-exercises-for-module-f-q1/">&lt;p&gt;Coding up a linked list based on raw pointers in Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 46: Exercises for module F (q1)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;60f7c162-33b6-402d-b22a-a49436e8aa29&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;strong&gt;46: Exercise 1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 45: Unsafe types and examples</title>
          <pubDate>Fri, 27 Sep 2024 19:07:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/09/27/rust-101-45-unsafe-types-and-examples/</link>
          <guid>https://artificialworlds.net/blog/2024/09/27/rust-101-45-unsafe-types-and-examples/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/09/27/rust-101-45-unsafe-types-and-examples/">&lt;p&gt;Looking through some of the types of code you will be working with if you&#x27;re doing unsafe Rust, and some of the unsafe types you might want to use.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 45: Unsafe types and examples&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6b9fae55-40e1-4159-8c99-d774d8086ff3&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;strong&gt;45: Unsafe types&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&#x2F;&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-48-exercises-for-module-f-q3&quot;&gt;48: Exercise 3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 44: Undefined behaviour</title>
          <pubDate>Mon, 16 Sep 2024 07:19:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/09/16/rust-101-44-undefined-behaviour/</link>
          <guid>https://artificialworlds.net/blog/2024/09/16/rust-101-44-undefined-behaviour/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/09/16/rust-101-44-undefined-behaviour/">&lt;p&gt;If you write unsafe Rust, you need to reason about &quot;undefined behaviour&quot;. We talk through what that means, and try to develop an intuition about why we can&#x27;t predict how our program will behave if we don&#x27;t follow the rules.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 44: Undefined behaviour&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;40f0e862-58bc-4fa2-8c48-e76ae005502d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;strong&gt;44: Undefined behaviour&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&#x2F;&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-48-exercises-for-module-f-q3&quot;&gt;48: Exercise 3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 43: The two meanings of &quot;unsafe&quot; in Rust</title>
          <pubDate>Sat, 07 Sep 2024 11:41:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/09/07/rust-101-43-the-two-meanings-of-unsafe-in-rust/</link>
          <guid>https://artificialworlds.net/blog/2024/09/07/rust-101-43-the-two-meanings-of-unsafe-in-rust/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/09/07/rust-101-43-the-two-meanings-of-unsafe-in-rust/">&lt;p&gt;The &lt;code&gt;unsafe&lt;&#x2F;code&gt; keyword in Rust means two things: &quot;You must read the docs!&quot; or &quot;I promise I read the docs and followed the rules!&quot;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 43: The two meanings of &amp;quot;unsafe&amp;quot; in Rust&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;2470d879-9f26-458d-8c0c-63d594b80989&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;42: Why unsafe?&lt;&#x2F;a&gt;, &lt;strong&gt;43: Meaning of unsafe&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&#x2F;&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-48-exercises-for-module-f-q3&quot;&gt;48: Exercise 3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 42: Why do we need unsafe?</title>
          <pubDate>Fri, 06 Sep 2024 07:55:42 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/09/06/rust-101-42-why-do-we-need-unsafe/</link>
          <guid>https://artificialworlds.net/blog/2024/09/06/rust-101-42-why-do-we-need-unsafe/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/09/06/rust-101-42-why-do-we-need-unsafe/">&lt;p&gt;There is a special mode in Rust programs called unsafe - why do we need it?&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 42: Why do we need unsafe?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0a29828b-9d30-4f12-a32b-7d1cd7c3859c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;strong&gt;Unsafe&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;strong&gt;42: Why unsafe?&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;07&#x2F;rust-101-43-the-two-meanings-of-unsafe-in-rust&#x2F;&quot;&gt;43: Meaning of unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;16&#x2F;rust-101-44-undefined-behaviour&#x2F;&quot;&gt;44: Undefined behaviour&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;27&#x2F;rust-101-45-unsafe-types-and-examples&#x2F;&quot;&gt;45: Unsafe types&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;07&#x2F;rust-101-46-exercises-for-module-f-q1&#x2F;&quot;&gt;46: Exercise 1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-47-exercises-for-module-f-q2&quot;&gt;47: Exercise 2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;21&#x2F;rust-101-48-exercises-for-module-f-q3&quot;&gt;48: Exercise 3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;F-safe-unsafe-rust&#x2F;&quot;&gt;Rust 101 - module F&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;F-safe-unsafe&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 41: Exercises for module E (q2b)</title>
          <pubDate>Thu, 22 Aug 2024 07:52:12 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/08/22/rust-101-41-exercises-for-module-e-q2b/</link>
          <guid>https://artificialworlds.net/blog/2024/08/22/rust-101-41-exercises-for-module-e-q2b/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/08/22/rust-101-41-exercises-for-module-e-q2b/">&lt;p&gt;Writing a mini client to connect to our async Rust chat server.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 41: Exercises for module E (q2b)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0e3f92c2-9afe-439e-8bd4-a86bc336f48f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;strong&gt;41: Exercise E2b&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 40: Exercises for module E (q2a)</title>
          <pubDate>Thu, 22 Aug 2024 07:38:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/08/22/rust-101-40-exercises-for-module-e-q2a/</link>
          <guid>https://artificialworlds.net/blog/2024/08/22/rust-101-40-exercises-for-module-e-q2a/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/08/22/rust-101-40-exercises-for-module-e-q2a/">&lt;p&gt;Writing a little chat server in async Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 40: Exercises for module E (q2a)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;93b4e1b1-def9-4b5a-bfcd-a350b9b4b2a7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;strong&gt;40: Exercise E2a&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 39: Exercises for module E (q1b)</title>
          <pubDate>Tue, 20 Aug 2024 17:59:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/08/20/rust-101-39-exercises-for-module-e-q1b/</link>
          <guid>https://artificialworlds.net/blog/2024/08/20/rust-101-39-exercises-for-module-e-q1b/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/08/20/rust-101-39-exercises-for-module-e-q1b/">&lt;p&gt;Writing a one-shot queue using async Rust, this time with a little less help.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 39: Exercises for module E (q1b)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ec7703c2-e087-467b-bc3a-6298ea326a1b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;strong&gt;39: Exercise E1b&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 38: Exercises for module E (q1a)</title>
          <pubDate>Tue, 06 Aug 2024 07:51:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/08/06/rust-101-38-exercises-for-module-e-q1a/</link>
          <guid>https://artificialworlds.net/blog/2024/08/06/rust-101-38-exercises-for-module-e-q1a/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/08/06/rust-101-38-exercises-for-module-e-q1a/">&lt;p&gt;Writing our own multi-producer-single-consumer (MPSC) queue using async Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 38: Exercises for module E (q1a)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;89d9afdb-f0d6-4a28-a0b4-2945432df5dd&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;strong&gt;38: Exercise E1a&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 37: Async runtimes</title>
          <pubDate>Mon, 29 Jul 2024 07:43:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/07/29/rust-101-37-async-runtimes/</link>
          <guid>https://artificialworlds.net/blog/2024/07/29/rust-101-37-async-runtimes/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/07/29/rust-101-37-async-runtimes/">&lt;p&gt;We talked about how Futures have poll methods, but who calls them? That is the job of the runtime. We talk about how to launch your async code and how to choose the right runtime, and then we have a very brief look at some Web frameworks that are based on async Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 37: Async runtimes&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0e39749b-5a1d-4ecd-b4c6-c30e1c64a9f5&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;strong&gt;37: Runtimes&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 36: What async and await really do</title>
          <pubDate>Thu, 25 Jul 2024 07:14:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/07/25/rust-101-36-what-async-and-await-really-do/</link>
          <guid>https://artificialworlds.net/blog/2024/07/25/rust-101-36-what-async-and-await-really-do/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/07/25/rust-101-36-what-async-and-await-really-do/">&lt;p&gt;Attempting to explain as slowly as possible what actually happens when the compiler finds an async function containing awaits: it writes a poll method for you to create something that implements Future. The generated poll method polls the Futures you asked to await, in order. The interesting bit is that this generated thing that implements Future and has a poll method needs to own the things it refers to, which is what leads to some of the most confusing error messages you see when writing this kind of code.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 36: What async and await really do&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4710fb61-47c8-4772-8f36-eafe03f69476&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;strong&gt;36: async&#x2F;await&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 35: Futures</title>
          <pubDate>Thu, 18 Jul 2024 07:47:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/07/18/rust-101-35-futures/</link>
          <guid>https://artificialworlds.net/blog/2024/07/18/rust-101-35-futures/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/07/18/rust-101-35-futures/">&lt;p&gt;Exploring what a Future is in async Rust and how we could manually write code that polls futures. Normally, we avoid this manual work by using the &lt;code&gt;async&lt;&#x2F;code&gt; and &lt;code&gt;await&lt;&#x2F;code&gt; keywords, but looking into this helps us understand what those keywords really do.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 35: Futures&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;376c92f0-56dd-4aaf-9115-b6b3cc8949e7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;34: What is async?&lt;&#x2F;a&gt;, &lt;strong&gt;35: Futures&lt;&#x2F;strong&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 34: What is async?</title>
          <pubDate>Thu, 18 Jul 2024 07:42:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/07/18/rust-101-34-what-is-async/</link>
          <guid>https://artificialworlds.net/blog/2024/07/18/rust-101-34-what-is-async/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/07/18/rust-101-34-what-is-async/">&lt;p&gt;What async programming is and what it looks like in Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 34: What is async?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c99e54e0-7391-4b31-a94d-1fa484d23f81&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;strong&gt;Async&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Async): &lt;strong&gt;34: What is async?&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-35-futures&#x2F;&quot;&gt;35: Futures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;25&#x2F;rust-101-36-what-async-and-await-really-do&#x2F;&quot;&gt;36: async&#x2F;await&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;29&#x2F;rust-101-37-async-runtimes&#x2F;&quot;&gt;37: Runtimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;06&#x2F;rust-101-38-exercises-for-module-e-q1a&#x2F;&quot;&gt;38: Exercise E1a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;20&#x2F;rust-101-39-exercises-for-module-e-q1b&#x2F;&quot;&gt;39: Exercise E1b&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-40-exercises-for-module-e-q2a&#x2F;&quot;&gt;40: Exercise E2a&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;08&#x2F;22&#x2F;rust-101-41-exercises-for-module-e-q2b&#x2F;&quot;&gt;41: Exercise E2b&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;E-async-web&quot;&gt;Rust 101 - module E&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;E-async-web&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 33: Exercises for module D (q3)</title>
          <pubDate>Mon, 24 Jun 2024 07:14:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/24/rust-101-33-exercises-for-module-d-q3/</link>
          <guid>https://artificialworlds.net/blog/2024/06/24/rust-101-33-exercises-for-module-d-q3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/24/rust-101-33-exercises-for-module-d-q3/">&lt;p&gt;Following through an exercise using a trait object with dynamic dispatch to choose different behaviour at runtime.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 33: Exercises for module D (q3)&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;58f88b19-983c-4af5-9b3f-78e3b44d47fc&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;28: Dynamic dispatch&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-29-trait-objects-and-object-safety&#x2F;&quot;&gt;29: Object safety&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-30-good-patterns-and-not-so-good&#x2F;&quot;&gt;30: Patterns&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;22&#x2F;rust-101-31-exercises-for-module-d-q1&#x2F;&quot;&gt;31: Exercise D1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;23&#x2F;rust-101-32-exercises-for-module-d-q2&#x2F;&quot;&gt;32: Exercise D2&lt;&#x2F;a&gt;, &lt;strong&gt;33: Exercise D3&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Elected to the Matrix Foundation Governing Board!</title>
          <pubDate>Sun, 23 Jun 2024 15:12:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/23/elected-to-the-matrix-foundation-governing-board/</link>
          <guid>https://artificialworlds.net/blog/2024/06/23/elected-to-the-matrix-foundation-governing-board/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/23/elected-to-the-matrix-foundation-governing-board/">&lt;p&gt;I am really excited to say I was elected to the Governing Board of the Matrix Foundation!&lt;&#x2F;p&gt;
&lt;p&gt;I was really surprised because there were lots of people standing, and the quality of the candidates was very high, but I am one of the lucky ones. I hope I can do a decent job of helping the foundation find its feet and bring about the change we want in modern communications. Thank you to everyone who voted for me: I will do my best.&lt;&#x2F;p&gt;
&lt;p&gt;As I wrote back in 2021, &lt;a href=&quot;&#x2F;blog&#x2F;2021&#x2F;05&#x2F;27&#x2F;matrix-is-the-only-chat-game-in-town&#x2F;&quot;&gt;Matrix is our best bet&lt;&#x2F;a&gt; to free ourselves from proprietary silos and realise the potential of private, secure communications between individuals and organisations across the world.&lt;&#x2F;p&gt;
&lt;p&gt;You can read my previous post to see the platform I ran on: &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;20&#x2F;standing-for-the-matrix-governing-board&#x2F;&quot;&gt;Standing for the Matrix Governing Board&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Since I wrote that there has been some very healthy discussion of the need for diversity in the Matrix community, and having been involved in diversity advocacy in several workplaces I am really keen to do what I can on this in the Matrix world. As an open standard we have a number of key advantages over other platforms that can make our software and our community beneficial for a wide variety of people.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reasons-why-matrix-can-be-better-for-diversity-than-closed-platforms&quot;&gt;Reasons why Matrix can be better for diversity than closed platforms&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Wide variety of implementations&lt;&#x2F;strong&gt; - since Matrix clients can be implemented by anyone, communities can have control over their software to make it work for them. This means specialised clients for people with different needs, and it also means experts can contribute to general clients to make them work better for more people. Our openness makes us stronger.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Public accountability&lt;&#x2F;strong&gt; - as a community working for everyone&#x27;s benefit, we are accountable to everyone for our behaviour. One of the jobs of the governing board will be to make sure marginalised voices are heard. Our openness makes us stronger!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Low cost&lt;&#x2F;strong&gt; - as an open source project there should be no financial barriers to entry for anyone wanting to use Matrix or contribute to it. Although lots of companies are building Matrix and building on Matrix for profit, one of the jobs of the foundation (as I see it) is to make sure we act as a truly open project, benefiting from the perspectives of people whose voices are silenced in high-cost ecosystems. &lt;strong&gt;Our openness makes us stronger!&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Additionally, because several governments fund and use Matrix, they often have strong provisions around accessibility: this can help hold all implementers to high standards.&lt;&#x2F;p&gt;
&lt;p&gt;However, we have a number of significant problems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;areas-where-matrix-is-lacking&quot;&gt;Areas where Matrix is lacking&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low diversity of contributors&lt;&#x2F;strong&gt; - along with much of the open source community, Matrix is missing lots of people from its community. Many contributors, like me, have highly privileged backgrounds making it difficult or impossible to serve the needs of under-represented people. Unless we make space and opportunities for people who have been marginalised to take the lead, we will fail, over and over, to fulfil our ambition to be a universal platform.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Safety and inappropriate content&lt;&#x2F;strong&gt; - many public Matrix servers are accidentally hosting illegal, offensive and inappropriate content, and are not always safe places for people of all ages to be. Although safety teams are working hard (for too little thanks!) every day to deal with this, the tooling for working in this area is sorely lacking, making the work much harder than it should be, and the teams are too small.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Lack of funds&lt;&#x2F;strong&gt; - the foundation and the companies working on Matrix do not have enough money to deal with the above problems. We need more funding from donations, corporations and governments to pay for work on diverse contributions, safety tooling, and safety staffing. So please &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;support&#x2F;&quot;&gt;donate to the foundation&lt;&#x2F;a&gt; and persuade your organisation to fund Matrix. How about paying for an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.outreachy.org&#x2F;&quot;&gt;Outreachy internship&lt;&#x2F;a&gt; as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2022&#x2F;08&#x2F;26&#x2F;outreachy-august-2022-update&#x2F;&quot;&gt;Element did&lt;&#x2F;a&gt; in 2022?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Please do comment below or contact me on Matrix (directly on @andybalaam:matrix.org or in a public room e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.to&#x2F;#&#x2F;#foundation-office:matrix.org&quot;&gt;#foundation-office:matrix.org&lt;&#x2F;a&gt;) if you&#x27;d like to discuss this more!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 32: Exercises for module D (q2)</title>
          <pubDate>Sun, 23 Jun 2024 14:20:27 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/23/rust-101-32-exercises-for-module-d-q2/</link>
          <guid>https://artificialworlds.net/blog/2024/06/23/rust-101-32-exercises-for-module-d-q2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/23/rust-101-32-exercises-for-module-d-q2/">&lt;p&gt;Trying out the typestate pattern by tracking the state of a 3D printer by changing our type instead of updating a variable whenever it changes.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 32: Exercises for module D (q2)&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;82020c95-1270-4235-ad48-a826e098511d&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;28: Dynamic dispatch&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-29-trait-objects-and-object-safety&#x2F;&quot;&gt;29: Object safety&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-30-good-patterns-and-not-so-good&#x2F;&quot;&gt;30: Patterns&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;22&#x2F;rust-101-31-exercises-for-module-d-q1&#x2F;&quot;&gt;31: Exercise D1&lt;&#x2F;a&gt;, &lt;strong&gt;32: Exercise D2&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;24&#x2F;rust-101-33-exercises-for-module-d-q3&#x2F;&quot;&gt;33: Exercise D3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 31: Exercises for module D (q1)</title>
          <pubDate>Sat, 22 Jun 2024 13:08:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/22/rust-101-31-exercises-for-module-d-q1/</link>
          <guid>https://artificialworlds.net/blog/2024/06/22/rust-101-31-exercises-for-module-d-q1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/22/rust-101-31-exercises-for-module-d-q1/">&lt;p&gt;Some good patterns to follow in your code e.g. &quot;newtype&quot;, &quot;typestate&quot; and one to avoid.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 31: Exercises for module D (q1)&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;bfbf6657-109a-4a2f-9e00-bbcb77b1ec82&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;28: Dynamic dispatch&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-29-trait-objects-and-object-safety&#x2F;&quot;&gt;29: Object safety&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-30-good-patterns-and-not-so-good&#x2F;&quot;&gt;30: Patterns&lt;&#x2F;a&gt;, &lt;strong&gt;31: Exercise D1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;23&#x2F;rust-101-32-exercises-for-module-d-q2&#x2F;&quot;&gt;32: Exercise D2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;24&#x2F;rust-101-33-exercises-for-module-d-q3&#x2F;&quot;&gt;33: Exercise D3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 30: Good patterns and not so good</title>
          <pubDate>Mon, 17 Jun 2024 07:47:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/17/rust-101-30-good-patterns-and-not-so-good/</link>
          <guid>https://artificialworlds.net/blog/2024/06/17/rust-101-30-good-patterns-and-not-so-good/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/17/rust-101-30-good-patterns-and-not-so-good/">&lt;p&gt;Some good patterns to follow in your code e.g. &quot;newtype&quot;, &quot;typestate&quot; and one to avoid.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 30: Good patterns and not so good&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d8af3592-6c9d-4899-978a-6f89543a8e8c&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;28: Dynamic dispatch&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-29-trait-objects-and-object-safety&#x2F;&quot;&gt;29: Object safety&lt;&#x2F;a&gt;, &lt;strong&gt;30: Patterns&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;22&#x2F;rust-101-31-exercises-for-module-d-q1&#x2F;&quot;&gt;31: Exercise D1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;23&#x2F;rust-101-32-exercises-for-module-d-q2&#x2F;&quot;&gt;32: Exercise D2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;24&#x2F;rust-101-33-exercises-for-module-d-q3&#x2F;&quot;&gt;33: Exercise D3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 29: Trait objects and object safety</title>
          <pubDate>Mon, 17 Jun 2024 07:45:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/17/rust-101-29-trait-objects-and-object-safety/</link>
          <guid>https://artificialworlds.net/blog/2024/06/17/rust-101-29-trait-objects-and-object-safety/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/17/rust-101-29-trait-objects-and-object-safety/">&lt;p&gt;Trying to explain why the rules for object safety are the way they are, and how to create and use a trait objects.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 29: Trait objects and object safety&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;f1d6124d-8829-4fc5-82a7-48681d433148&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;28: Dynamic dispatch&lt;&#x2F;a&gt;, &lt;strong&gt;29: Object safety&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-30-good-patterns-and-not-so-good&#x2F;&quot;&gt;30: Patterns&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;22&#x2F;rust-101-31-exercises-for-module-d-q1&#x2F;&quot;&gt;31: Exercise D1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;23&#x2F;rust-101-32-exercises-for-module-d-q2&#x2F;&quot;&gt;32: Exercise D2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;24&#x2F;rust-101-33-exercises-for-module-d-q3&#x2F;&quot;&gt;33: Exercise D3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 28: Dynamic dispatch</title>
          <pubDate>Mon, 10 Jun 2024 18:36:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/10/rust-101-28-dynamic-dispatch/</link>
          <guid>https://artificialworlds.net/blog/2024/06/10/rust-101-28-dynamic-dispatch/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/10/rust-101-28-dynamic-dispatch/">&lt;p&gt;Explaining how to hold on to something even when we don&#x27;t know its exact type, just what trait it implements.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 28: Dynamic dispatch&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;5bebdea7-e5f5-47c0-a534-1938b0a9c23e&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot; width=&quot;560&quot; height=&quot;315&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;Trait objects&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Trait objects): &lt;strong&gt;28: Dynamic dispatch&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-29-trait-objects-and-object-safety&#x2F;&quot;&gt;29: Object safety&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;17&#x2F;rust-101-30-good-patterns-and-not-so-good&#x2F;&quot;&gt;30: Patterns&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;22&#x2F;rust-101-31-exercises-for-module-d-q1&#x2F;&quot;&gt;31: Exercise D1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;23&#x2F;rust-101-32-exercises-for-module-d-q2&#x2F;&quot;&gt;32: Exercise D2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;24&#x2F;rust-101-33-exercises-for-module-d-q3&#x2F;&quot;&gt;33: Exercise D3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;D-trait-objects-patterns&quot;&gt;Rust 101 - module D&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;D-trait-objects-patterns&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New home for my videos: video.infosec.exchange</title>
          <pubDate>Sun, 09 Jun 2024 12:39:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/06/09/new-home-for-my-videos-video-infosec-exchange/</link>
          <guid>https://artificialworlds.net/blog/2024/06/09/new-home-for-my-videos-video-infosec-exchange/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/06/09/new-home-for-my-videos-video-infosec-exchange/">&lt;p&gt;Huge thanks to Micah Scott for hosting my videos on diode.zone until recently.&lt;&#x2F;p&gt;
&lt;p&gt;My videos are moving to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;a&#x2F;andybalaam&quot;&gt;video.infosec.exchange&#x2F;a&#x2F;andybalaam&lt;&#x2F;a&gt; - please update all your feeds. (Thanks to Jerry Bell for running that - please &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;about&#x2F;instance#business-model&quot;&gt;donate&lt;&#x2F;a&gt;!)&lt;&#x2F;p&gt;
&lt;p&gt;You can follow this blog for an &quot;always&quot; reliable source of truth for my stuff.&lt;&#x2F;p&gt;
&lt;p&gt;Following me on Mastodon at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt; is likely to be pretty reliable too.&lt;&#x2F;p&gt;
&lt;p&gt;I will remove the live recordings from diode.zone quite soon, to free up space. I will leave the lectures up there for as long as possible, but they may go away at any time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I won&#x27;t link to AI resources</title>
          <pubDate>Fri, 24 May 2024 08:58:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/24/why-i-wont-link-to-ai-resources/</link>
          <guid>https://artificialworlds.net/blog/2024/05/24/why-i-wont-link-to-ai-resources/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/24/why-i-wont-link-to-ai-resources/">&lt;p&gt;I received a very kind email today from someone who had found my page &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;01&#x2F;09&#x2F;resources-for-year-6-teachers-on-coding-and-programming&#x2F;&quot;&gt;Resources for year 6 teachers on coding and programming&lt;&#x2F;a&gt; helpful, and wanted to suggest another link for me to add, about AI resources.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m sure it was a helpful and useful link, but I didn&#x27;t feel able to add it. Here&#x27;s the email I wrote explaining why:&lt;&#x2F;p&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for your email, and I&#x27;m really glad you found the resources on my web site helpful.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m very sceptical about AI, and so cautious about recommending resources about it. Of course, I think it&#x27;s important for students to learn about the techniques involved and the potential benefits and harms it can have for our society, but before I could link to resources I would need to do lots of research to be sure I could stand behind what was being said.&lt;&#x2F;p&gt;
&lt;p&gt;If you are surprised to hear I feel quite negatively about AI, my concerns fall in three different areas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;taking other people&#x27;s work and passing it off as your own - systems like ChatGPT and DALL-E consume vast amounts of copyrighted material and produce output that is derived from it without any reward for the people who created it.&lt;&#x2F;li&gt;
&lt;li&gt;incorrect results, dangerous behaviour and bias - many companies are promoting AI assistants and even AI &quot;friends&quot;, making bold statements about their reliability, despite numerous example of bots being &quot;tricked&quot; into saying dangerous things (e.g. promoting suicide) or simply acting in biased ways (e.g. Amazon&#x27;s recruiting AI that was biased against women). Machine learning models are characterised by the fact that we don&#x27;t know how they work, so we can&#x27;t make any supportable claims about how they will behave.&lt;&#x2F;li&gt;
&lt;li&gt;environmental impact - training these models takes vast amounts of energy and water. These costs are currently being hidden by the huge amount of money being invested in the hope that there will be a great return in terms of value. I don&#x27;t think much value has been generated so far...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, I believe we will eventually integrate AI into our society, and hopefully in a responsible way (although I have my doubts) but right now I see it as mostly a massive hype cycle, like cryptocurrency and &quot;the cloud&quot; before it.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m sorry I can&#x27;t provide links to back up my arguments, but I am suffering from the same problem: I don&#x27;t have time to check my sources properly so I can be sure I&#x27;m sending you reliable information. I do encourage you to research further!&lt;&#x2F;p&gt;
&lt;p&gt;So, apologies for not being able to help (and for writing you an essay!) but I can&#x27;t add the link you so kindly suggested.&lt;&#x2F;p&gt;
&lt;p&gt;Good luck in your project,&lt;&#x2F;p&gt;
&lt;p&gt;Andy&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 27: Exercises for module C (q2)</title>
          <pubDate>Thu, 23 May 2024 08:05:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/23/rust-101-27-exercises-for-module-c-q2/</link>
          <guid>https://artificialworlds.net/blog/2024/05/23/rust-101-27-exercises-for-module-c-q2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/23/rust-101-27-exercises-for-module-c-q2/">&lt;p&gt;Implementing a simplified form of Mutex.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 27: Exercises for module C (q2)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a6a589be-9ce2-4cb7-ad53-54679275865c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;strong&gt;Concurrency and parallelism&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Concurrency and parallelism): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;24: Parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;16&#x2F;rust-101-25-threads-mutexes-channels-send-and-sync&#x2F;&quot;&gt;25: Threads&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;22&#x2F;rust-101-26-exercises-for-module-c-q1&#x2F;&quot;&gt;26: Exercise C1&lt;&#x2F;a&gt;, &lt;strong&gt;27: Exercise C2&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;C-concurrency-parallelism&quot;&gt;Rust 101 - module C&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 26: Exercises for module C (q1)</title>
          <pubDate>Wed, 22 May 2024 19:22:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/22/rust-101-26-exercises-for-module-c-q1/</link>
          <guid>https://artificialworlds.net/blog/2024/05/22/rust-101-26-exercises-for-module-c-q1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/22/rust-101-26-exercises-for-module-c-q1/">&lt;p&gt;Searching across multiple documents in parallel with Rayon.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 26: Exercises for module C (q1)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a144b49f-86e4-4795-ad32-fe006039eced&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;strong&gt;Concurrency and parallelism&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Concurrency and parallelism): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;24: Parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;16&#x2F;rust-101-25-threads-mutexes-channels-send-and-sync&#x2F;&quot;&gt;25: Threads&lt;&#x2F;a&gt;, &lt;strong&gt;26: Exercise C1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;23&#x2F;rust-101-27-exercises-for-module-c-q2&#x2F;&quot;&gt;27: Exercise C2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;C-concurrency-parallelism&quot;&gt;Rust 101 - module C&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Choosing who to vote for in the 2024 UK General Election</title>
          <pubDate>Wed, 22 May 2024 19:18:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/22/choosing-who-to-vote-for-in-the-2024-uk-general-election/</link>
          <guid>https://artificialworlds.net/blog/2024/05/22/choosing-who-to-vote-for-in-the-2024-uk-general-election/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/22/choosing-who-to-vote-for-in-the-2024-uk-general-election/">&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: I just discussed this with my son and I really want to emphasise:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;It does make a difference who is in power.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The previous Labour government significantly improved primary education and many other public services. Don&#x27;t lose hope!&lt;&#x2F;p&gt;
&lt;p&gt;Here are the criteria I will use to select which party to vote for in the 2024 UK General Election.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;competence&quot;&gt;Competence&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting against the Conservative party because it has comprehensively proven itself to be incompetent at managing both the economy and public services.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ideological-blindness&quot;&gt;Ideological blindness&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting against the Conservative party because its policies have been driven by blind ideology (e.g. the Truss economic disaster) and&#x2F;or the perceived prejudices of their core voters (e.g. the &quot;ship them off to Rwanda&quot; immigration policy).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;public-services&quot;&gt;Public services&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting for a party that I believe is committed to improving our public services, especially the NHS, education and prisons. This will involve channelling funds when available, but also good-faith work to change working practices and improve working conditions and outcomes for patients, pupils and prisoners.&lt;&#x2F;p&gt;
&lt;p&gt;For bonus points I&#x27;d like to see water companies and rail services gradually moved into public control. Transport For London offers an excellent model for public&#x2F;private partnership in rail. Water is a total mess so I&#x27;m looking for a party that is willing to take ownership for fixing the problems with water and sewage management.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;climate&quot;&gt;Climate&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting for a party that I believe is willing to invest optimistically in the energy transition to realise the climate and economic benefits of making significant progress soon. I want regulation and financial incentives that punish polluting and carbon-emitting behaviour and invest in innovation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;immigration&quot;&gt;Immigration&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting for a party who I trust to speak truthfully about immigration: we benefit economically from immigration, and we rely on immigration in many areas, not least universities. We should manage economic migration, working to integrate people and working with local councils to provide housing and public services where people are arriving. Pretending we don&#x27;t need immigrants makes us unable to tackle the real strains large numbers of arrivals can put on a particular area.&lt;&#x2F;p&gt;
&lt;p&gt;We should establish safe routes for refugees to travel to the UK, and we should urgently act to process the backlog of asylum claims. We must remove the backlog if we want to discourage spurious asylum claims. It may even be necessary to declare an amnesty if that is the only way to clear the backlog.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;foreign-policy&quot;&gt;Foreign policy&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting for a party that has a grown-up attitude to foreign policy: responding to the threat from hostile actors like Russia, and speaking out over matters of human rights. I remember when there was debate about whether South African apartheid was &quot;any of our business&quot;. History has shown that it was our business, and we should have applied maximum non-military pressure to bring apartheid to an end. I believe the situation in Israel&#x2F;Palestine will be viewed similarly when we have hindsight, and we should act now with public statements, trade sanctions and legal action.&lt;&#x2F;p&gt;
&lt;p&gt;We should use our influence to advocate for those who are oppressed across the world, and (of course) we should lead by example with the highest standards of justice and fairness in our own country.&lt;&#x2F;p&gt;
&lt;p&gt;We should stop arms sales to Saudi Arabia and Israel and significantly tighten regulation around arms sales to countries engaged in military action that fails to minimise harm to civilians.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;housing&quot;&gt;Housing&lt;&#x2F;h2&gt;
&lt;p&gt;I will be voting for a party who are willing to make the changes needed to provide more houses. This will involve changing rules to make it easier to build homes, and to incentivise building homes, not sitting on land. It will also likely involve the government or councils building and renting quality homes at low rents.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-universal-basic-income&quot;&gt;Bonus: universal basic income&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;d love to see someone propose a serious plan for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Universal_basic_income&quot;&gt;universal basic income&lt;&#x2F;a&gt;. Our benefits system already acknowledges the right of all people to food, housing, water and other services.&lt;&#x2F;p&gt;
&lt;p&gt;Our health system gives freedom to everyone to plan for their future life without dreading a massive bill if something goes wrong. Let&#x27;s give a similar freedom to everyone that if their job or business idea falls through, they can still survive. Imagine the explosion of innovation and artistic expression that could spring from this freedom!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 25: Threads, Mutexes, channels, Send and Sync</title>
          <pubDate>Thu, 16 May 2024 19:20:59 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/16/rust-101-25-threads-mutexes-channels-send-and-sync/</link>
          <guid>https://artificialworlds.net/blog/2024/05/16/rust-101-25-threads-mutexes-channels-send-and-sync/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/16/rust-101-25-threads-mutexes-channels-send-and-sync/">&lt;p&gt;How to spawn threads and deal with lifetimes, how to send or share state across threads, and what Send and Sync mean.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 25: Threads, Mutexes, channels, Send and Sync&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a189293c-e3f3-4617-87c3-d3800d1ec996&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;strong&gt;Concurrency and parallelism&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Concurrency and parallelism): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;24: Parallelism&lt;&#x2F;a&gt;, &lt;strong&gt;25: Threads&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;22&#x2F;rust-101-26-exercises-for-module-c-q1&#x2F;&quot;&gt;26: Exercise C1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;23&#x2F;rust-101-27-exercises-for-module-c-q2&#x2F;&quot;&gt;27: Exercise C2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;C-concurrency-parallelism&quot;&gt;Rust 101 - module C&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 24: Parallelism and Rayon</title>
          <pubDate>Mon, 13 May 2024 08:50:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/13/rust-101-24-parallelism-and-rayon/</link>
          <guid>https://artificialworlds.net/blog/2024/05/13/rust-101-24-parallelism-and-rayon/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/13/rust-101-24-parallelism-and-rayon/">&lt;p&gt;What concurrency and parallelism are, a brief intro to Rayon, and a quick note on closures.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 24: Parallelism and Rayon&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fecec481-0fab-4dd5-9689-861d40982d97&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;strong&gt;Concurrency and parallelism&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Concurrency and parallelism): &lt;strong&gt;24: Parallelism&lt;&#x2F;strong&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;05&#x2F;16&#x2F;rust-101-25-threads-mutexes-channels-send-and-sync&#x2F;&quot;&gt;25: Threads&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;22&#x2F;rust-101-26-exercises-for-module-c-q1&#x2F;&quot;&gt;26: Exercise C1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;23&#x2F;rust-101-27-exercises-for-module-c-q2&#x2F;&quot;&gt;27: Exercise C2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;C-concurrency-parallelism&quot;&gt;Rust 101 - module C&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;C-concurrency-parallelism&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 23 Exercises for module B (q3)</title>
          <pubDate>Sat, 04 May 2024 07:42:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/04/rust-101-23-exercises-for-module-b-q3/</link>
          <guid>https://artificialworlds.net/blog/2024/05/04/rust-101-23-exercises-for-module-b-q3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/04/rust-101-23-exercises-for-module-b-q3/">&lt;p&gt;Testing, benchmarking and optimising a small program that plays FizzBuzz.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 23: Exercises for module B (q3)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3cadfbc5-c2a2-4b93-815e-a6cdf989c7ec&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;18: Dependencies&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;18&#x2F;rust-101-19-creating-a-nice-api&#x2F;&quot;&gt;19: API design&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;22&#x2F;rust-101-20-unit-integration-and-benchmark-tests&#x2F;&quot;&gt;20: Tests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-21-exercises-for-module-b-q1&#x2F;&quot;&gt;21: Exercise B1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-22-exercises-for-module-b-q2&#x2F;&quot;&gt;22: Exercise B2&lt;&#x2F;a&gt;, &lt;strong&gt;23: Exercise B3&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 22 Exercises for module B (q2)</title>
          <pubDate>Sat, 04 May 2024 07:42:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/04/rust-101-22-exercises-for-module-b-q2/</link>
          <guid>https://artificialworlds.net/blog/2024/05/04/rust-101-22-exercises-for-module-b-q2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/04/rust-101-22-exercises-for-module-b-q2/">&lt;p&gt;Building a command-line quiz application to bed in our knowledge about crates, modules and serialisation. All done using test-driven development.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 22: Exercises for module B (q2)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;431c9187-8a29-4f3a-9727-b65cfcf979aa&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;18: Dependencies&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;18&#x2F;rust-101-19-creating-a-nice-api&#x2F;&quot;&gt;19: API design&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;22&#x2F;rust-101-20-unit-integration-and-benchmark-tests&#x2F;&quot;&gt;20: Tests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-21-exercises-for-module-b-q1&#x2F;&quot;&gt;21: Exercise B1&lt;&#x2F;a&gt;, &lt;strong&gt;22: Exercise B2&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-23-exercises-for-module-b-q3&#x2F;&quot;&gt;23: Exercise B3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 21 Exercises for module B (q1)</title>
          <pubDate>Sat, 04 May 2024 07:41:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/05/04/rust-101-21-exercises-for-module-b-q1/</link>
          <guid>https://artificialworlds.net/blog/2024/05/04/rust-101-21-exercises-for-module-b-q1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/05/04/rust-101-21-exercises-for-module-b-q1/">&lt;p&gt;Going through an exercise on serialization with serde.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 21: Exercises for module B (q1)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6be2aee4-18ea-4c29-968f-4c95048b1fb7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;18: Dependencies&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;18&#x2F;rust-101-19-creating-a-nice-api&#x2F;&quot;&gt;19: API design&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;22&#x2F;rust-101-20-unit-integration-and-benchmark-tests&#x2F;&quot;&gt;20: Tests&lt;&#x2F;a&gt;, &lt;strong&gt;21: Exercise B1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-22-exercises-for-module-b-q2&#x2F;&quot;&gt;22: Exercise B2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-23-exercises-for-module-b-q3&#x2F;&quot;&gt;23: Exercise B3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 20: Unit, integration and benchmark tests</title>
          <pubDate>Mon, 22 Apr 2024 07:50:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/04/22/rust-101-20-unit-integration-and-benchmark-tests/</link>
          <guid>https://artificialworlds.net/blog/2024/04/22/rust-101-20-unit-integration-and-benchmark-tests/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/04/22/rust-101-20-unit-integration-and-benchmark-tests/">&lt;p&gt;How to write unit tests in your Rust code, and some quick pointers on writing integration and benchmark tests.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 20: Unit, integration and benchmark tests&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;be615c45-5505-400e-b994-1da850e704ae&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;18: Dependencies&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;18&#x2F;rust-101-19-creating-a-nice-api&#x2F;&quot;&gt;19: API design&lt;&#x2F;a&gt;, &lt;strong&gt;20: Tests&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-21-exercises-for-module-b-q1&#x2F;&quot;&gt;21: Exercise B1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-22-exercises-for-module-b-q2&#x2F;&quot;&gt;22: Exercise B2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-23-exercises-for-module-b-q3&#x2F;&quot;&gt;23: Exercise B3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Standing for the Matrix Governing Board</title>
          <pubDate>Sat, 20 Apr 2024 08:42:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/04/20/standing-for-the-matrix-governing-board/</link>
          <guid>https://artificialworlds.net/blog/2024/04/20/standing-for-the-matrix-governing-board/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/04/20/standing-for-the-matrix-governing-board/">&lt;p&gt;I have decided to stand for election to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;blog&#x2F;2024&#x2F;04&#x2F;election-announcement&#x2F;&quot;&gt;Matrix Governing Board&lt;&#x2F;a&gt;, which is a brand new body which advises and oversees the work of the Matrix Foundation. I will stand as an Individual Member, not representing a company or project.&lt;&#x2F;p&gt;
&lt;p&gt;Contents:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;04&#x2F;20&#x2F;standing-for-the-matrix-governing-board&#x2F;#purpose-of-the-board&quot;&gt;Purpose of the board&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;04&#x2F;20&#x2F;standing-for-the-matrix-governing-board&#x2F;#my-qualifications-and-interests&quot;&gt;My qualifications and interests&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;04&#x2F;20&#x2F;standing-for-the-matrix-governing-board&#x2F;#my-biases&quot;&gt;My biases&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2024&#x2F;04&#x2F;20&#x2F;standing-for-the-matrix-governing-board&#x2F;#my-priorities&quot;&gt;My priorities&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;purpose-of-the-board&quot;&gt;Purpose of the board&lt;&#x2F;h2&gt;
&lt;p&gt;The board will operate on an advisory basis, but it will be responsible for approving budgets, projects and partnerships that are significant to the future of the foundation.&lt;&#x2F;p&gt;
&lt;p&gt;I see the Governing Board as having two major purposes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Speaking up if the foundation starts down the wrong course.&lt;&#x2F;strong&gt; Hopefully it won&#x27;t happen, but if the foundation makes choices that are harmful to Matrix or the community, the board can shout about it (but not actually block it).&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Guiding direction for the best interests of the community.&lt;&#x2F;strong&gt; The board will consist of people with personal and professional interest in the success of Matrix, and with expertise in many areas, so I hope and expect it will provide advice and guidance that help the foundation channel its funding and effort towards that success.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;my-qualifications-and-interests&quot;&gt;My qualifications and interests&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;I am a long-standing &lt;strong&gt;contributor to many Free&#x2F;Open Source projects&lt;&#x2F;strong&gt;. My contribution history dates back to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;FreeGuide&#x2F;commit&#x2F;74e185826b778ef0cb7a758bf6016300364f7356&quot;&gt;at least 2001&lt;&#x2F;a&gt; and I have been an enthusiastic supporter of the principles of Software Freedom for many years.&lt;&#x2F;li&gt;
&lt;li&gt;I am a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&#x2F;sponsors&#x2F;#sustainers&quot;&gt;sustainer&lt;&#x2F;a&gt; of the &lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&quot;&gt;Software Freedom Conservancy&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt; as well as a Matrix Foundation individual member.&lt;&#x2F;li&gt;
&lt;li&gt;As part of my work for Element I have contributed across many &lt;strong&gt;Matrix projects&lt;&#x2F;strong&gt; including &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;pulls?q=+is%3Apr+author%3Aandybalaam+&quot;&gt;MSCs&lt;&#x2F;a&gt; and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;spec&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-js-sdk&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;JS SDK&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-react-sdk&#x2F;pulls?q=is%3Apr+author%3Aandybalaam&quot;&gt;React SDK&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rust-sdk&#x2F;pulls?q=author%3Aandybalaam+&quot;&gt;Rust SDK&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;complement-crypto&#x2F;pulls?q=is%3Apr+author%3Aandybalaam+&quot;&gt;Crypto tests&lt;&#x2F;a&gt; and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rich-text-editor&#x2F;pulls?q=is%3Apr+author%3Aandybalaam+&quot;&gt;rich text editor&lt;&#x2F;a&gt;, along with many others.&lt;&#x2F;li&gt;
&lt;li&gt;I am a &lt;strong&gt;good communicator&lt;&#x2F;strong&gt;, and have made many popular &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;a&#x2F;andybalaam&#x2F;video-channels&quot;&gt;programming videos&lt;&#x2F;a&gt; (released under Free Culture licenses).&lt;&#x2F;li&gt;
&lt;li&gt;I am known in the places I have worked as a &lt;strong&gt;highly collaborative&lt;&#x2F;strong&gt; team member, bringing organisational abilities and people skills along with my technical expertise.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can find out more about me at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-biases&quot;&gt;My biases&lt;&#x2F;h2&gt;
&lt;p&gt;I am an employee of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;element.io&#x2F;&quot;&gt;Element&lt;&#x2F;a&gt;, meaning it is in my interest for Element to prosper. Since one of the purposes of the Governing Board is to increase the independence of the Matrix Foundation from Element, this could be a downside to my involvement.&lt;&#x2F;p&gt;
&lt;p&gt;I have a long work and free software history outside Element which I think will help me balance the different interests, and I commit to remove myself from decisions whenever I think that my employment situation might cause a conflict of interest. This means if I think on a particular topic Element&#x27;s interests might be in conflict with the foundation&#x27;s I will remove myself from the decision-making on that topic and make a short statement explaining why.&lt;&#x2F;p&gt;
&lt;p&gt;In practice, I believe that Element is a critical supporter of Matrix, and the purpose of its founding was to make Matrix successful, so I expect that in most cases there will be no conflict. (It&#x27;s worth noting that if you disagree with that, you should probably vote against me!)&lt;&#x2F;p&gt;
&lt;p&gt;My other biases are towards Software Freedom and open standards. I like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Copyleft&quot;&gt;Copyleft&lt;&#x2F;a&gt; licenses, I prefer to avoid the use of non-free platforms (e.g. GitHub) and I am normally against strong &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Contributor_License_Agreement&quot;&gt;CLA&lt;&#x2F;a&gt;s.&lt;&#x2F;p&gt;
&lt;p&gt;I am an enthusiastic supporter of Matrix because I can imagine a world where everyone can communicate securely and privately with their friends and family, companies, medical services, governments and anyone else, free of snooping and manipulation by powerful individual companies. In this world, we can talk privately, do business, and administrate our lives without worrying about who is excluded, who is listening, and whether we are the product. It&#x27;s a little miracle that so many people have access to email, and it&#x27;s time to level up to real-time, secure communication that is free from unwanted control and influence.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-priorities&quot;&gt;My priorities&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;I want Matrix to last.&lt;&#x2F;strong&gt; When I look at the success of the Linux project, I see years of steady work paying off in the long term. The project put its attention into making an excellent piece of software, and &quot;taking over the world&quot; came slowly and naturally as a by-product of that. I think Matrix should take the same approach: steadily working on creating an excellent open standard and excellent implementations of it. Taking over the world (which is absolutely my ambition!) will inevitably come if the product is good enough, because the advantages of openness are so strong.&lt;&#x2F;li&gt;
&lt;li&gt;For Matrix to last, the foundation needs &lt;strong&gt;financial independence&lt;&#x2F;strong&gt;. I want to see the foundation raise more money, both from individuals and from corporate and government users. I&#x27;d like to see the foundation itself fund development of the standard and some implementations. This would help development focus on the features that are of most benefit to the whole community, and, critically, it would support the &quot;boring&quot; work of maintenance and high quality.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you like the idea of my voice helping to guide the Matrix Foundation, please vote for me!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 19: Creating a nice API</title>
          <pubDate>Thu, 18 Apr 2024 18:15:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/04/18/rust-101-19-creating-a-nice-api/</link>
          <guid>https://artificialworlds.net/blog/2024/04/18/rust-101-19-creating-a-nice-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/04/18/rust-101-19-creating-a-nice-api/">&lt;p&gt;Tips and rules for writing good APIs that are easy for other people to use. Try to make them Unsurprising, Flexible, and Obvious.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 19: Creating a nice API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b1a37c19-3784-4107-a715-ee8d54871732&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;18: Dependencies&lt;&#x2F;a&gt;, &lt;strong&gt;19: API design&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;22&#x2F;rust-101-20-unit-integration-and-benchmark-tests&#x2F;&quot;&gt;20: Tests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-21-exercises-for-module-b-q1&#x2F;&quot;&gt;21: Exercise B1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-22-exercises-for-module-b-q2&#x2F;&quot;&gt;22: Exercise B2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-23-exercises-for-module-b-q3&#x2F;&quot;&gt;23: Exercise B3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 18: Dependencies and Cargo.toml</title>
          <pubDate>Thu, 11 Apr 2024 08:27:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/04/11/rust-101-18-dependencies-and-cargo-toml/</link>
          <guid>https://artificialworlds.net/blog/2024/04/11/rust-101-18-dependencies-and-cargo-toml/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/04/11/rust-101-18-dependencies-and-cargo-toml/">&lt;p&gt;How to describe details of your Rust project with a Cargo.toml file, and how to find and add dependencies (other people&#x27;s code).&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 18: Dependencies and Cargo.toml&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;55f765fc-0581-4cd7-9ae0-3937bd54d857&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;strong&gt;Building applications&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Building applications): &lt;strong&gt;18: Dependencies&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;18&#x2F;rust-101-19-creating-a-nice-api&#x2F;&quot;&gt;19: API design&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;22&#x2F;rust-101-20-unit-integration-and-benchmark-tests&#x2F;&quot;&gt;20: Tests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-21-exercises-for-module-b-q1&#x2F;&quot;&gt;21: Exercise B1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-22-exercises-for-module-b-q2&#x2F;&quot;&gt;22: Exercise B2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;04&#x2F;rust-101-23-exercises-for-module-b-q3&#x2F;&quot;&gt;23: Exercise B3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;B-application-programming&quot;&gt;Rust 101 - module B&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;B-application-programming&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 17: Exercises for module A3 (part 2)</title>
          <pubDate>Mon, 08 Apr 2024 08:26:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/04/08/rust-101-17-exercises-for-module-a3-part-2/</link>
          <guid>https://artificialworlds.net/blog/2024/04/08/rust-101-17-exercises-for-module-a3-part-2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/04/08/rust-101-17-exercises-for-module-a3-part-2/">&lt;p&gt;Finishing off the exercises on Rust traits, designing a customised version of Vec.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 17: Exercises for module A3 (part 2)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;aa642b1b-010c-4782-b3b5-9be030da96a2&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;12: Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;22&#x2F;rust-101-13-type-parameters-and-associated-types&#x2F;&quot;&gt;13: Type Params&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;07&#x2F;rust-101-14-some-standard-library-traits&#x2F;&quot;&gt;14: std Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;14&#x2F;rust-101-15-lifetime-bounds&#x2F;&quot;&gt;15: Lifetimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;23&#x2F;rust-101-16-exercises-for-module-a3-part-1&#x2F;&quot;&gt;16: Exercises A3pt1&lt;&#x2F;a&gt;, &lt;strong&gt;17: Exercises A3pt2&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - module A3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Automated backups from Signal to Nextcloud</title>
          <pubDate>Fri, 29 Mar 2024 10:17:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/03/29/automated-backups-from-signal-to-nextcloud/</link>
          <guid>https://artificialworlds.net/blog/2024/03/29/automated-backups-from-signal-to-nextcloud/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/03/29/automated-backups-from-signal-to-nextcloud/">&lt;p&gt;DON&#x27;T DO THIS: the Signal app has the ability to make daily backups of your messages, and when you choose a folder to backup into, you can choose &quot;Nextcloud&quot; as the device, then choose a Nextcloud folder to back up to.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Don&#x27;t do that.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Although the above worked for me for a long time, a few weeks ago, Signal started making 0-byte files for all my backups, and merrily deleting the old ones.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, choose a normal folder on your phone and ask Signal to back up to there.&lt;&#x2F;p&gt;
&lt;p&gt;Then open the Nextcloud app, go to Settings, Auto-uploads, and add a custom folder to auto-upload. For Local Folder, choose the folder you just told Signal to back up into, and for the Remote Folder choose somewhere in Nextcloud to store your backups.&lt;&#x2F;p&gt;
&lt;p&gt;Nextcloud will automatically notice whenever a new backup file appears and copy it to your server.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 16: Exercises for module A3 (part 1)</title>
          <pubDate>Sat, 23 Mar 2024 16:34:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/03/23/rust-101-16-exercises-for-module-a3-part-1/</link>
          <guid>https://artificialworlds.net/blog/2024/03/23/rust-101-16-exercises-for-module-a3-part-1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/03/23/rust-101-16-exercises-for-module-a3-part-1/">&lt;p&gt;Going through some exercises on Rust traits, designing a customised version of Vec.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 16: Exercises for module A3 (part 1)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ff064e0c-fe93-48b1-8298-fc8e49fee80b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;12: Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;22&#x2F;rust-101-13-type-parameters-and-associated-types&#x2F;&quot;&gt;13: Type Params&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;07&#x2F;rust-101-14-some-standard-library-traits&#x2F;&quot;&gt;14: std Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;14&#x2F;rust-101-15-lifetime-bounds&#x2F;&quot;&gt;15: Lifetimes&lt;&#x2F;a&gt;, &lt;strong&gt;16: Exercises A3pt1&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;08&#x2F;rust-101-17-exercises-for-module-a3-part-2&#x2F;&quot;&gt;17: Exercises A3pt2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - module A3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>One import per line is best</title>
          <pubDate>Fri, 22 Mar 2024 14:34:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/03/22/one-import-per-line/</link>
          <guid>https://artificialworlds.net/blog/2024/03/22/one-import-per-line/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/03/22/one-import-per-line/">&lt;p&gt;Rust has a feature where if you import two things from the same module you can abbreviate it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;use mypkg::{MyStruct1, MyStruct2};&lt;&#x2F;pre&gt;
&lt;p&gt;If you prefer, you can keep them separate, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;use mypkg::MyStruct1;
use mypkg::MyStruct2;&lt;&#x2F;pre&gt;
&lt;p&gt;I do prefer. Strongly.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;advantages-of-the-abbreviated-style&quot;&gt;Advantages of the abbreviated style&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Fewer lines of code&lt;&#x2F;li&gt;
&lt;li&gt;Fewer characters of code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;advantages-of-the-separated-style&quot;&gt;Advantages of the separated style&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simpler diffs with fewer conflicts.&lt;&#x2F;strong&gt; If I add or delete an import, it adds or removes exactly one line. This makes it easier to read diffs and reduces conflicts when merging.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Less diff noise when lines overflow.&lt;&#x2F;strong&gt; Adding or removing an import is less likely to cause the code formatter to reflow the line. Reflows make it hard to see which dependencies have changed when reading a diff.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Easier searching.&lt;&#x2F;strong&gt; If I&#x27;m trying to find out where a particular type is used I can search for its full path, and my search will find all the places I import it.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Easier to read.&lt;&#x2F;strong&gt; I find line of code like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rust-sdk&#x2F;blob&#x2F;main&#x2F;crates&#x2F;matrix-sdk-crypto&#x2F;src&#x2F;store&#x2F;memorystore.rs#L34&quot;&gt;this&lt;&#x2F;a&gt; really hard to read:&lt;pre&gt;use crate::{
gossiping::{GossipRequest, GossippedSecret, SecretInfo},
identities::{ReadOnlyDevice, ReadOnlyUserIdentities},
olm::{OutboundGroupSession, PrivateCrossSigningIdentity},
types::events::room_key_withheld::RoomKeyWithheldEvent,
TrackedUser,
};&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I think the separated style is much better, and I wish Rustfmt defaulted to using it. What do you think?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rustfmt-config&quot;&gt;rustfmt config&lt;&#x2F;h2&gt;
&lt;p&gt;To enforce my preferred style, include this in your rustfmt.toml:&lt;&#x2F;p&gt;
&lt;pre&gt;imports_granularity = Item&lt;&#x2F;pre&gt;
&lt;p&gt;(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rustfmt&#x2F;?version=v1.6.0&amp;amp;search=#imports_granularity&quot;&gt;Rustfmt docs&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 15: Lifetime bounds</title>
          <pubDate>Thu, 14 Mar 2024 09:27:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/03/14/rust-101-15-lifetime-bounds/</link>
          <guid>https://artificialworlds.net/blog/2024/03/14/rust-101-15-lifetime-bounds/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/03/14/rust-101-15-lifetime-bounds/">&lt;p&gt;This time we tackle one of the most tricky areas for a new Rust programmer: lifetimes. The key point is that when we add lifetime bounds (&#x27;a or similar) to a function signature, this is not to help Rust compile our function: it&#x27;s to help Rust understand the lifetime of our return values, so that it knows how to compile the code that calls this function. The lifetime bounds tell the compiler about the references we return - specifically, how they relate to the references that were passed in.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 15: Lifetime bounds&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;09bac01f-9b59-4bf0-b01b-6191ae7161d7&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;12: Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;22&#x2F;rust-101-13-type-parameters-and-associated-types&#x2F;&quot;&gt;13: Type Params&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;07&#x2F;rust-101-14-some-standard-library-traits&#x2F;&quot;&gt;14: std Traits&lt;&#x2F;a&gt;, &lt;strong&gt;15: Lifetimes&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;23&#x2F;rust-101-16-exercises-for-module-a3-part-1&#x2F;&quot;&gt;16: Exercises A3pt1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;08&#x2F;rust-101-17-exercises-for-module-a3-part-2&#x2F;&quot;&gt;17: Exercises A3pt2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - 15: Lifetime bounds&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 14: Some standard library traits</title>
          <pubDate>Thu, 07 Mar 2024 09:47:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/03/07/rust-101-14-some-standard-library-traits/</link>
          <guid>https://artificialworlds.net/blog/2024/03/07/rust-101-14-some-standard-library-traits/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/03/07/rust-101-14-some-standard-library-traits/">&lt;p&gt;A tour of some of the most interesting traits in the standard library including Add etc. to overload operators, Sized, Sync and Send for telling the compiler special things about your type, Clone and Copy for copying things, Into and From for converting, AsRef and AsMut for making your APIs flexible, Default for easy creation, and Drop which is called when your object is deleted. Also a few words on what happens when you call a function with generic type parameters.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 14: Some standard library traits&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1e3728a0-a33a-4c9a-a772-a433feee963e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;12: Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;22&#x2F;rust-101-13-type-parameters-and-associated-types&#x2F;&quot;&gt;13: Type Params&lt;&#x2F;a&gt;, &lt;strong&gt;14: std Traits&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;14&#x2F;rust-101-15-lifetime-bounds&#x2F;&quot;&gt;15: Lifetimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;23&#x2F;rust-101-16-exercises-for-module-a3-part-1&#x2F;&quot;&gt;16: Exercises A3pt1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;08&#x2F;rust-101-17-exercises-for-module-a3-part-2&#x2F;&quot;&gt;17: Exercises A3pt2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - 14: Some standard library traits&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 13: Type Parameters and Associated Types</title>
          <pubDate>Thu, 22 Feb 2024 09:43:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/02/22/rust-101-13-type-parameters-and-associated-types/</link>
          <guid>https://artificialworlds.net/blog/2024/02/22/rust-101-13-type-parameters-and-associated-types/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/02/22/rust-101-13-type-parameters-and-associated-types/">&lt;p&gt;Following on from &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;video 12&lt;&#x2F;a&gt;, looking at how to add type information to traits, to make them flexible enough to describe generic code with different types, for example how to add a u32 to a u64 and return a u128, without defining a whole new trait for every combination of types.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 13: Type Parameters and Associated Types&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e2dc2b28-4a12-4bdf-9080-e1881ccffc6a&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;,&lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;12: Traits&lt;&#x2F;a&gt;, &lt;strong&gt;13: Type Params&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;07&#x2F;rust-101-14-some-standard-library-traits&#x2F;&quot;&gt;14: std Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;14&#x2F;rust-101-15-lifetime-bounds&#x2F;&quot;&gt;15: Lifetimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;23&#x2F;rust-101-16-exercises-for-module-a3-part-1&#x2F;&quot;&gt;16: Exercises A3pt1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;08&#x2F;rust-101-17-exercises-for-module-a3-part-2&#x2F;&quot;&gt;17: Exercises A3pt2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - 13: Type Parameters and Associated Types&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 12: Traits</title>
          <pubDate>Mon, 19 Feb 2024 08:57:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/02/19/rust-101-12-traits/</link>
          <guid>https://artificialworlds.net/blog/2024/02/19/rust-101-12-traits/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/02/19/rust-101-12-traits/">&lt;p&gt;Explaining what a trait is, and how to use it. A trait is a bit like an Interface in Java or Go, or an Abstract Base Class in C++ or Python, but it can be used to define behaviour at compile-time as well as at run-time. We go through an example of why you might want to write a &quot;generic&quot; function - one that works for lots of different types, and if so, how you need to be able to say what the types can do if you want to write the body of the function. Traits are a way of saying what a type can do.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 12: Traits&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6f82f0aa-9d11-4496-a570-c67f24b69f18&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;strong&gt;Traits and generics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Traits and generics): &lt;strong&gt;12: Traits&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;22&#x2F;rust-101-13-type-parameters-and-associated-types&#x2F;&quot;&gt;13: Type params&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;07&#x2F;rust-101-14-some-standard-library-traits&#x2F;&quot;&gt;14: std Traits&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;14&#x2F;rust-101-15-lifetime-bounds&#x2F;&quot;&gt;15: Lifetimes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;03&#x2F;23&#x2F;rust-101-16-exercises-for-module-a3-part-1&#x2F;&quot;&gt;16: Exercises A3pt1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;08&#x2F;rust-101-17-exercises-for-module-a3-part-2&#x2F;&quot;&gt;17: Exercises A3pt2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A3-traits-generics&quot;&gt;Rust 101 - 12: Traits and slices&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A3-traits-generics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 11: Exercises for module A2</title>
          <pubDate>Wed, 07 Feb 2024 08:51:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/02/07/rust-101-11-exercises-for-module-a2/</link>
          <guid>https://artificialworlds.net/blog/2024/02/07/rust-101-11-exercises-for-module-a2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/02/07/rust-101-11-exercises-for-module-a2/">&lt;p&gt;Going through some exercises on Rust ownership, references, slices and error handling.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 11: Exercises for module A2&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;61233729-8eff-4e6e-b19d-d20e65247862&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;strong&gt;11: Exercises A2&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Rust 101 is a series of videos explaining how to write programs in Rust. The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>[Fixed in FF 123] Deleting an Indexed DB store can be incredibly slow on Firefox</title>
          <pubDate>Fri, 02 Feb 2024 10:17:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/02/02/deleting-an-indexed-db-store-can-be-incredibly-slow-on-firefox/</link>
          <guid>https://artificialworlds.net/blog/2024/02/02/deleting-an-indexed-db-store-can-be-incredibly-slow-on-firefox/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/02/02/deleting-an-indexed-db-store-can-be-incredibly-slow-on-firefox/">&lt;p&gt;&lt;strong&gt;Update: as confirmed in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=1878312&quot;&gt;bug I logged&lt;&#x2F;a&gt;, this was fixed in Firefox 123!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;See also: &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;24&#x2F;keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance&#x2F;&quot;&gt;Keep your Indexed DB keys and values small if you want good performance!&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead&#x2F;&quot;&gt;Don&#x27;t store arrays of numbers in Indexed DB - use base64 instead&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We had &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;element-web&#x2F;issues&#x2F;26948&quot;&gt;performance problem&lt;&#x2F;a&gt; in Element Web when upgrading the Indexed DB schema, and it turned out that in Firefox, deleting an object store can be incredibly slow. It can take tens of minutes or even hours.&lt;&#x2F;p&gt;
&lt;p&gt;(In Chromium the same operation can take tens of seconds, but it&#x27;s way, way faster.)&lt;&#x2F;p&gt;
&lt;p&gt;I was expecting this to be a near-instant operation, so this was a definite surprise to me.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Note that my analysis is based on widely-available browsers in February 2024, and will probably become out-of-date.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can see the full graphs here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;indexed-db-perf&#x2F;delete.html&quot;&gt;artificialworlds.net&#x2F;indexed-db-perf&#x2F;delete.html&lt;&#x2F;a&gt;. Source code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I learned:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-1-firefox-can-take-a-very-long-time-to-delete-an-object-store&quot;&gt;Headline 1: Firefox can take a very long time to delete an object store&lt;&#x2F;h2&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;firefox-delete-index.png&quot; alt=&quot;Graph showing that Firefox took 1.2 million milliseconds to delete an object store containing 200K records.&quot; width=&quot;497&quot; height=&quot;299&quot; class=&quot;aligncenter size-full wp-image-3782&quot;&gt;
&lt;p&gt;Firefox took 20 minutes to delete an object store containing 200K records, with one index.&lt;&#x2F;p&gt;
&lt;p&gt;In Chromium, I found that deleting a similar object store took 21 seconds, which is still slow, but rather more acceptable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Consider not deleting stores you don&#x27;t need any more. If you must delete them, you will need to provide feedback to your users, especially if they are using Firefox.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-2-clearing-the-store-before-deleting-it-really-helps&quot;&gt;Headline 2: Clearing the store before deleting it really helps(!)&lt;&#x2F;h2&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;firefox-clear-delete-index.png&quot; alt=&quot;Graph showing that Firefox takes 1.2 million ms to delete a large object store (200K records), or 300 thousand ms if you clear it before deleting it.&quot; width=&quot;578&quot; height=&quot;319&quot; class=&quot;aligncenter size-full wp-image-3784&quot;&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;chromium-clear-delete-index.png&quot; alt=&quot;Graph showing that Chromium takes 20 thousand ms to delete a large object store (200K records), or 10 thousand ms if you clear it before deleting it.&quot; width=&quot;549&quot; height=&quot;324&quot; class=&quot;aligncenter size-full wp-image-3785&quot;&gt;
&lt;p&gt;On both Firefox and Chromium, running objectStore.clear() before upgrading the db and deleting the store made a significant improvement to the total time. On Chromium it more than halved the time to delete the store, and on Firefox (where the numbers were huge) it reduced the time by about 3 times!&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to michael-p on Stack Overflow for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;43215199&#x2F;indexeddb-onupgradeneeded-event-never-finishes&quot;&gt;giving me the idea&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Clear your store before deleting it.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-3-with-no-indices-this-is-fine&quot;&gt;Headline 3: With no indices, this is fine&lt;&#x2F;h2&gt;
&lt;p&gt;Firefox deletes object stores fairly fast if there are no indices (and so does Chromium).&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;firefox-delete-no-index.png&quot; alt=&quot;Graph showing that even for 200K records, Firefox can delete an object store in under 500ms if there is no index on it.&quot; width=&quot;497&quot; height=&quot;310&quot; class=&quot;aligncenter size-full wp-image-3781&quot;&gt;
&lt;p&gt;Even for 200K records, Firefox can delete an object store in under 500ms if there is no index on it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;If you need fast deletion, don&#x27;t use indices.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;observation-it-s-not-done-until-the-db-is-closed&quot;&gt;Observation: It&#x27;s not done until the DB is closed&lt;&#x2F;h2&gt;
&lt;p&gt;In my timing experiments, I found that objectStore.delete() completed, but the operation was not really done. When I called the close() method on my IDBDatabase I had to wait a whole lot longer. (The close time is included in the measurements above.)&lt;&#x2F;p&gt;
&lt;p&gt;Even when I refreshed the browser, I found I had a long wait to open the Indexed DB database. After the wait, it worked fine and the schema update was complete.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Expect long close times after a deletion.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Don&#x27;t store normal arrays of numbers in Indexed DB - use UInt8Array instead</title>
          <pubDate>Thu, 25 Jan 2024 13:52:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/25/dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead/</link>
          <guid>https://artificialworlds.net/blog/2024/01/25/dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/25/dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead/">&lt;p&gt;Following on from &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;24&#x2F;keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance&#x2F;&quot;&gt;Keep your Indexed DB keys and values small if you want good performance!&lt;&#x2F;a&gt;, here is another thing I&#x27;ve learned about Indexed DB performance (in July 2024):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;richvdh.org&#x2F;&quot;&gt;richvdh&lt;&#x2F;a&gt;, we now know UInt8Array is much better than base64!&lt;&#x2F;p&gt;
&lt;p&gt;If you have a long list of numbers to store, don&#x27;t put them in a JavaScript array - instead put them into a UInt8Array. Even encoding them to base64 strings is better than normal arrays.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the numbers:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;arrays-firefox2.png&quot; alt=&quot;Graph showing that arrays of numbers are much slower to count than the same numbers encoded as base64, and UInt8Arrays are even better, in Firefox.&quot; width=&quot;716&quot; height=&quot;403&quot; class=&quot;aligncenter size-full wp-image-4303&quot;&gt;
&lt;p&gt;&lt;strong&gt;On Firefox, storing the same set of numbers as an array is much slower than encoding them as base64, and both are easily beaten by UInt8Arrays.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;arrays-chromium2.png&quot; alt=&quot;Graph showing that arrays of numbers are much slower to count than the same numbers encoded as base64, and UInt8Arrays are even better, in Chromium.&quot; width=&quot;716&quot; height=&quot;403&quot; class=&quot;aligncenter size-full wp-image-4304&quot;&gt;
&lt;p&gt;&lt;strong&gt;On Chromium, storing the same set of numbers as an array is much slower than encoding them as base64, and both are easily beaten by UInt8Arrays.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;These graphs show that in both Firefox and Chromium (at time of writing), it is much faster to count the records in an Indexed DB store if the list of numbers inside each record is encoded as a base64 string instead of being included directly as a JavaScript array, but the best performance comes from using a UInt8Array.&lt;&#x2F;p&gt;
&lt;p&gt;The interactive 3D(!) graphs are here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;indexed-db-perf&#x2F;arrays.html&quot;&gt;artificialworlds.net&#x2F;indexed-db-perf&#x2F;arrays.html&lt;&#x2F;a&gt; and the source code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;See also: &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;24&#x2F;keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance&#x2F;&quot;&gt;Keep your Indexed DB keys and values small if you want good performance!&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;02&#x2F;deleting-an-indexed-db-store-can-be-incredibly-slow-on-firefox&quot;&gt;Deleting an Indexed DB store can be incredibly slow on Firefox&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 10: Strings</title>
          <pubDate>Thu, 25 Jan 2024 09:26:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/25/rust-101-10-strings/</link>
          <guid>https://artificialworlds.net/blog/2024/01/25/rust-101-10-strings/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/25/rust-101-10-strings/">&lt;p&gt;What a String is in Rust, and how they differ from &amp;amp;str. Strings are resizeable arrays of bytes that are guaranteed to be in UTF-8 format. &amp;amp;strs are references to chunks of bytes that are also guaranteed to be in UTF-8 format.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to learn more about UTF-8 and character encodings, check out my video &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;24&#x2F;interesting-characters-utf-16-utf-8-unicode-encodings-video&#x2F;&quot;&gt;Interesting Characters&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 10: Strings&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d33a9480-2b5a-421e-8b59-2c913ac48d0e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;strong&gt;10: Strings&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 9: Vecs, Boxes and slices&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Keep your Indexed DB keys and values small if you want good performance!</title>
          <pubDate>Wed, 24 Jan 2024 14:50:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/24/keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance/</link>
          <guid>https://artificialworlds.net/blog/2024/01/24/keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/24/keep-your-indexed-db-keys-and-values-small-if-you-want-good-performance/">&lt;p&gt;In our work recently on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;element-hq&#x2F;element-web&quot;&gt;Element Web&lt;&#x2F;a&gt; (specifically attempting to replace our encryption code with our &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-rust-sdk&quot;&gt;cross-platform Rust implementation&lt;&#x2F;a&gt;) we&#x27;ve noticed some strange behaviour with the performance of our Indexed DB queries.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve been aware of some slowdowns, and wondered whether it was related to the locks we take in the Rust code (which is compiled to WASM for this project), but actually we&#x27;ve tracked the latest problems down to time spent inside Indexed DB queries.&lt;&#x2F;p&gt;
&lt;p&gt;The first surprising thing we found was that several of our slow queries were just counting the number of records in a store. As we dug deeper, we began to suspect that the size of the values we were storing was affecting performance.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;interactive-graphs.png&quot; alt=&quot;Click the link for interactive graphs&quot; width=&quot;387&quot; height=&quot;320&quot; class=&quot;aligncenter size-full wp-image-3740&quot;&gt;
&lt;p&gt;I designed a simple test page and we measured some results. You can look at interactive graphs and and record on your own device here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;indexed-db-perf&quot;&gt;artificialworlds.net&#x2F;indexed-db-perf&lt;&#x2F;a&gt;. I am hoping to expand this page to more devices and more operations over time, so do check back and&#x2F;or contribute results. Source code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;indexed-db-perf&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Note that my analysis is based on widely-available browsers in January 2024, and may become out-of-date.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here are my conclusions so far.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-1-counting-the-records-in-your-store-can-be-slow&quot;&gt;Headline 1: counting the records in your store can be slow&lt;&#x2F;h2&gt;
&lt;p&gt;If we have large keys or values, and we try find out how many records there are in a store containing 200K of them, it will be very slow (6 seconds on my Chromium).&lt;&#x2F;p&gt;
&lt;p&gt;Try to avoid counting records when you can, and consider caching the count in your own code if you need it often.&lt;&#x2F;p&gt;
&lt;p&gt;The slowness of counting probably also indicates that operations that walk all records will also be slow, so think carefully about when and whether you need to do that, and expect to provide feedback to your users when you do it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Don&#x27;t count or walk all records unless you have to.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-2-long-keys-hurt-performance&quot;&gt;Headline 2: long keys hurt performance&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;indexed-db-perf&quot;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;long-keys-hurt-perf.png&quot; alt=&quot;3D graph showing that long keys (2000) and large numbers of records (200K) result in 800ms time to count records.&quot; width=&quot;338&quot; height=&quot;351&quot; class=&quot;aligncenter size-full wp-image-3738&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;On Firefox, as keys get longer, performance with large numbers of records becomes slower.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The time it takes to count records grows linearly with the length of the keys, and becomes large when keys go over 50 bytes (500 bytes in Firefox).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The shorter your keys, the better.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-3-large-values-hurt-performance&quot;&gt;Headline 3: large values hurt performance&lt;&#x2F;h2&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;large-values-hurt-perf.png&quot; alt=&quot;Graph showing it can take 6 seconds to count records with large values (50K) and lots of records (200K(&quot; width=&quot;339&quot; height=&quot;333&quot; class=&quot;aligncenter size-full wp-image-3745&quot;&gt;
&lt;p&gt;&lt;strong&gt;On Chrome, as values get larger, performance with large numbers of records becomes slower.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The time it takes to count records grows linearly with the size of the values you are storing, and becomes large when values go over 1K in size (10K in Firefox).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The smaller your values, the better.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-4-the-number-of-records-matters&quot;&gt;Headline 4: the number of records matters&lt;&#x2F;h2&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;keep-below-50k-records.png&quot; alt=&quot;Graphs showing the performance on Firefox degrades rapidly after around 50K records.&quot; width=&quot;631&quot; height=&quot;307&quot; class=&quot;aligncenter size-full wp-image-3748&quot;&gt;
&lt;p&gt;&lt;strong&gt;On Firefox over 50K records (with large keys or values), performance degrades rapidly.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you are storing large values (&amp;gt; 1K) or large keys (&amp;gt; 50 bytes), and you need to walk all records regularly, you should probably try to keep the number of records below 50K if you want interactive performance - over that it rapidly increases to the order of seconds.&lt;&#x2F;p&gt;
&lt;p&gt;In Firefox on Intel, it does seem reasonably stable after 50K, though, so if you exclusively target Firefox on Intel, larger stores are feasible, but you will need to manage the user experience around these ~1 second delays.&lt;&#x2F;p&gt;
&lt;p&gt;In Chromium, performance continues to degrade as the number of records increases. It reaches truly terrible times (6 seconds) with large keys (2000 bytes) and large numbers (200K).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Keep below 50K records if you can.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;headline-5-indexed-db-performance-is-currently-very-poor-on-apple-m1&quot;&gt;Headline 5: Indexed DB performance is currently very poor on Apple M1&lt;&#x2F;h2&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;m1-chrome-is-slow.png&quot; alt=&quot;Graph showing counting records can take 12 seconds on an Apple M1.&quot; width=&quot;333&quot; height=&quot;340&quot; class=&quot;aligncenter size-full wp-image-3749&quot;&gt;
&lt;p&gt;&lt;strong&gt;Chrome on Apple M1 hardware takes 12 seconds to perform operations that take 2.5 seconds on Intel.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Firefox&#x27;s indexed DB performance on Apple M1 is ~10 times slower than on Intel, taking 8 seconds to count 200K large records.&lt;&#x2F;p&gt;
&lt;p&gt;Chrome&#x27;s performance with large keys (&amp;gt; 1000 bytes) is fairly catastrophic when the number of rows approaches 50K. It takes 12 seconds to count 200K records with keys of length 2000, ~6x slower than on Intel.&lt;&#x2F;p&gt;
&lt;p&gt;Both browsers on M1 hit a wall at about 50K records, where their performance with either large keys or large values rapidly degrades to almost unusable levels. The only option for decent performance is to keep keys short and values small.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Test on Apple M1 - it has a serious problem right now!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;observation-firefox-faster-than-chromium&quot;&gt;Observation: Firefox faster than Chromium&lt;&#x2F;h2&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;firefox-faster-than-chromium.png&quot; alt=&quot;Graph showing that for counting large numbers of large records, Firefox takes 0.8 seconds and Chromium takes 6 seconds.&quot; width=&quot;662&quot; height=&quot;358&quot; class=&quot;aligncenter size-full wp-image-3750&quot;&gt;
&lt;p&gt;&lt;strong&gt;Chrome takes 6 seconds to count large numbers of large values, and Firefox takes 0.8 seconds to do the same thing.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;At time of writing, Firefox&#x27;s Indexed DB appears to be much faster than Chromium&#x27;s when dealing with large numbers of records. In some scenarios, over 7 times faster.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;side-note-string-values-are-better-than-arrays-of-numbers&quot;&gt;Side note: string values are better than arrays of numbers&lt;&#x2F;h2&gt;
&lt;p&gt;Update: see &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead&#x2F;&quot;&gt;Don’t store arrays of numbers in Indexed DB - use base64 instead&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t have the formal evidence for this because I ran out of time for my investigation, but my initial exploration suggests that encoding a string as an array of numbers, one for each character, slows down performance by over 10 times.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;(I think) you should use strings instead of arrays of numbers.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;p&gt;Watch out for Apple M1!&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, if you need to improve the performance of your Indexed DB-based web site, you have three options:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Store fewer records,&lt;&#x2F;li&gt;
&lt;li&gt;Reduce the size of the records, or&lt;&#x2F;li&gt;
&lt;li&gt;Reduce the length of the keys.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;1 and 2 are probably harder than 3. So definitely consider 3 first.&lt;&#x2F;p&gt;
&lt;p&gt;If you keep key length and value length small, you can have lots of records.&lt;&#x2F;p&gt;
&lt;p&gt;Explore the graphs here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;indexed-db-perf&quot;&gt;artificialworlds.net&#x2F;indexed-db-perf&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;See also: &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;dont-store-arrays-of-numbers-in-indexed-db-use-base64-instead&#x2F;&quot;&gt;Don&#x27;t store arrays of numbers in Indexed DB - use base64 instead&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;02&#x2F;deleting-an-indexed-db-store-can-be-incredibly-slow-on-firefox&quot;&gt;Deleting an Indexed DB store can be incredibly slow on Firefox&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 9: Vecs, Boxes and slices</title>
          <pubDate>Thu, 18 Jan 2024 09:27:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/18/rust-101-9-vecs-boxes-and-slices/</link>
          <guid>https://artificialworlds.net/blog/2024/01/18/rust-101-9-vecs-boxes-and-slices/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/18/rust-101-9-vecs-boxes-and-slices/">&lt;p&gt;Explanation of some of the most commonly used types in Rust: Vecs, which store lists of items, Boxes that allow us to own things that we keep on the heap, and slices that are a way of referring to parts of Vecs or arrays without owning them.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 9: Vecs, Boxes and slices&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b3823452-f559-48d2-8ff9-652d32237c23&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;strong&gt;9: Vec and Box&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 9: Vecs, Boxes and slices&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 8: Writing methods using impl blocks</title>
          <pubDate>Thu, 18 Jan 2024 09:18:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/18/rust-101-8-writing-methods-using-impl-blocks/</link>
          <guid>https://artificialworlds.net/blog/2024/01/18/rust-101-8-writing-methods-using-impl-blocks/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/18/rust-101-8-writing-methods-using-impl-blocks/">&lt;p&gt;This time we discuss how to add methods to structs and enums, using impl blocks. Methods work similarly to other languages, but it might be a surprise that they can be defined inside separate blocks, and in fact they can be defined in multiple different blocks, which can be useful, and can also be a potential cause of confusion!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 8: Writing methods using impl blocks&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;33fdfd2a-4926-4f99-8431-af2e5be9889e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;strong&gt;8: Methods&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 8: Writing methods using impl blocks&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2024 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 7: Error handling with panic and Result</title>
          <pubDate>Thu, 11 Jan 2024 08:58:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2024/01/11/rust-101-7-error-handling-with-panic-and-result/</link>
          <guid>https://artificialworlds.net/blog/2024/01/11/rust-101-7-error-handling-with-panic-and-result/</guid>
          <description xml:base="https://artificialworlds.net/blog/2024/01/11/rust-101-7-error-handling-with-panic-and-result/">&lt;p&gt;We learnt about enums and generics &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;last time&lt;&#x2F;a&gt;, which means we&#x27;re ready to talk about Result, which is a really nice way of handling errors in Rust, that allows you to be very explicit about what went wrong, but also with a very compact syntax using the ? operator. Before that we discuss &lt;code&gt;panic!&lt;&#x2F;code&gt;, which essentially stops your program with an error message, and is useful for handling errors that should never happen.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 7: Error handling with panic and Result&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d2457979-5508-4fd6-8757-04f4a9c0e380&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;strong&gt;7: Panic and Result&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 7: Error handling with panic and Result slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP: using starvation as a weapon</title>
          <pubDate>Mon, 18 Dec 2023 15:15:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/12/18/letter-to-my-mp-using-starvation-as-a-weapon/</link>
          <guid>https://artificialworlds.net/blog/2023/12/18/letter-to-my-mp-using-starvation-as-a-weapon/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/12/18/letter-to-my-mp-using-starvation-as-a-weapon/">&lt;p&gt;Dear Ben Spencer,&lt;&#x2F;p&gt;
&lt;p&gt;The behaviour of Hamas on 7th October and afterwards is inexcusable, and is rightly condemned by our government.&lt;&#x2F;p&gt;
&lt;p&gt;I believe that the ongoing behaviour of the Israeli government in response to these attacks is inexcusable, and should be condemned in the strongest terms.&lt;&#x2F;p&gt;
&lt;p&gt;Human Rights Watch reports:&lt;&#x2F;p&gt;
&lt;p&gt;* Israeli Prime Minister Benjamin Netanyahu said on October 19 that Israel would not allow humanitarian assistance “in the form of food and medicines” into Gaza through its crossings “as long as our hostages are not returned.”&lt;&#x2F;p&gt;
&lt;p&gt;* What’s been happening on the ground, backed by such top-level statements from Israeli leaders, is “reflecting an intent to starve civilians as a method of warfare&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;* Israel’s continuing blockade of Gaza, not to mention its illegal closure of Gaza for more than 16 years, amounts to collective punishment of the civilian population.&lt;&#x2F;p&gt;
&lt;p&gt;* The government of Israel needs to immediately stop weaponizing starvation, and concerned governments should call on Israel to do so.&lt;&#x2F;p&gt;
&lt;p&gt;(Reference: https:&#x2F;&#x2F;www.hrw.org&#x2F;the-day-in-human-rights&#x2F;2023&#x2F;12&#x2F;18?story=paragraph-7339 )&lt;&#x2F;p&gt;
&lt;p&gt;Please urge our government to condemn these actions, and work for a lasting peace.&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
&lt;p&gt;(Feel free to re-use my wording in similar letters.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Santa Circles 0.3 is out!</title>
          <pubDate>Tue, 07 Nov 2023 09:31:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/11/07/santa-circles-0-3-is-out/</link>
          <guid>https://artificialworlds.net/blog/2023/11/07/santa-circles-0-3-is-out/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/11/07/santa-circles-0-3-is-out/">&lt;p&gt;Santa Circles 0.3 is all new and shiny!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;63e8abf05f4cae38.png&quot; alt=&quot;&amp;quot;Organise circle&amp;quot;
Circle Name: Alice&#x27;s Party
Draw Date: 15th November 2023
Rules: Spend no more than £10&quot; width=&quot;393&quot; height=&quot;239&quot; class=&quot;aligncenter size-full wp-image-3690&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;santacircles.artificialworlds.net&quot;&gt;santacircles.artificialworlds.net&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(It&#x27;s a secret-santa-style gift exchange web site.)&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t worry, it looks exactly how it did last year, except there is a tiny &quot;Forgot password?&quot; link you can click if you need it.&lt;&#x2F;p&gt;
&lt;p&gt;It also has some useful new abilities for people organising circles: the ability to copy an old circle, and visibility of who has made a wishlist (and who needs nagging).&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s still zero JavaScript, and it&#x27;s all very FOSS and self-hostable.&lt;&#x2F;p&gt;
&lt;p&gt;The code is at:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;santa-circles&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;santa-circles&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;santa-circles-api&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;santa-circles-api&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Follow this blog on the fediverse!</title>
          <pubDate>Thu, 12 Oct 2023 08:16:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/10/12/follow-this-blog-on-the-fediverse/</link>
          <guid>https://artificialworlds.net/blog/2023/10/12/follow-this-blog-on-the-fediverse/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/10/12/follow-this-blog-on-the-fediverse/">&lt;p&gt;If I&#x27;ve set it up right, you can now follow this blog on Mastodon and other fediverse platforms!&lt;&#x2F;p&gt;
&lt;p&gt;Search for this user:&lt;&#x2F;p&gt;
&lt;pre&gt;@blog@www.artificialworlds.net&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 6: Structs and Enums</title>
          <pubDate>Thu, 05 Oct 2023 16:53:32 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/10/05/rust-101-6-structs-and-enums/</link>
          <guid>https://artificialworlds.net/blog/2023/10/05/rust-101-6-structs-and-enums/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/10/05/rust-101-6-structs-and-enums/">&lt;p&gt;Continuing our review of the things you need in Rust to write programs - grouping together data using structs, and allowing multiple possibilities with enums. We look at how to decide which thing you&#x27;ve got with the match keyword, and review a very popular enum called Option, which lets you say either you&#x27;ve got something, or you&#x27;ve got nothing.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 6: Structs and Enums&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b1a71e26-5445-401d-8204-4b3dc2eb8fea&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;5: References&lt;&#x2F;a&gt;, &lt;strong&gt;6: Structs and Enums&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 6: Structs and Enums slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 5: References</title>
          <pubDate>Sat, 30 Sep 2023 08:38:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/09/30/rust-101-5-references/</link>
          <guid>https://artificialworlds.net/blog/2023/09/30/rust-101-5-references/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/09/30/rust-101-5-references/">&lt;p&gt;Starting some more advanced Rust programming ideas by looking at references - the ability to &quot;borrow&quot; values and refer to them without taking ownership. We look at mutable and immutable references, and the rules about references that prevent us ever having an &quot;invalid&quot; reference that points at something that has been dropped or changed.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 5: References&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ffbfb255-5b9c-4be3-99d4-cf33dcdd86ed&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;More syntax&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (More syntax): &lt;strong&gt;5: References&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;10&#x2F;05&#x2F;rust-101-6-structs-and-enums&#x2F;&quot;&gt;6: Structs and Enums&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;11&#x2F;rust-101-7-error-handling-with-panic-and-result&#x2F;&quot;&gt;7: Panic and Result&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-8-writing-methods-using-impl-blocks&#x2F;&quot;&gt;8: Methods&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;18&#x2F;rust-101-9-vecs-boxes-and-slices&#x2F;&quot;&gt;9: Vec and Box&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;01&#x2F;25&#x2F;rust-101-10-strings&#x2F;&quot;&gt;10: Strings&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;07&#x2F;rust-101-11-exercises-for-module-a2&#x2F;&quot;&gt;11: Exercises A2&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A2-advanced-intro&quot;&gt;Rust 101 - 5: References slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A2-advanced-intro&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 4: Exercises for module A1</title>
          <pubDate>Sat, 30 Sep 2023 07:28:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/09/30/rust-101-4-exercises-for-module-ai/</link>
          <guid>https://artificialworlds.net/blog/2023/09/30/rust-101-4-exercises-for-module-ai/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/09/30/rust-101-4-exercises-for-module-ai/">&lt;p&gt;Going through some exercises on basic Rust syntax and ownership.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 4: Exercises for module A1&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c9ce94e9-4bd2-470c-b6d1-21c2051ab7be&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To see the exercises code as it was when I recorded the video:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;git clone https:&#x2F;&#x2F;github.com&#x2F;trifectatechfoundation&#x2F;teach-rs.git
cd teach-rs
git checkout 95d9927
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Series: &lt;strong&gt;Language basics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Language basics): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;1: Intro&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-2-language-basics&#x2F;&quot;&gt;2: Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-3-memory-and-ownership&#x2F;&quot;&gt;3: Memory and ownership&lt;&#x2F;a&gt;, &lt;strong&gt;4: Exercises A1&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A1-intro-to-rust&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A1-intro-to-rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Rust 101 is a series of videos explaining how to write programs in Rust. The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 3: Memory and ownership</title>
          <pubDate>Mon, 11 Sep 2023 17:16:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/09/11/rust-101-3-memory-and-ownership/</link>
          <guid>https://artificialworlds.net/blog/2023/09/11/rust-101-3-memory-and-ownership/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/09/11/rust-101-3-memory-and-ownership/">&lt;p&gt;Continuing on Rust programming basics by looking at ownership and memory management, including the stack and the heap: what they are, how they differ, and why you need to care.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 3: Memory and ownership&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b953baa5-a63c-40c1-8411-7e30407b65e1&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;For more help on ownership and the stack and the heap, try &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;book&#x2F;ch04-00-understanding-ownership.html&quot;&gt;Chapter 4 of the Rust book&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Series: &lt;strong&gt;Language basics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Language basics): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;1: Intro&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-2-language-basics&#x2F;&quot;&gt;2: Language basics&lt;&#x2F;a&gt;, &lt;strong&gt;3: Memory and ownership&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-4-exercises-for-module-ai&#x2F;&quot;&gt;4: Exercises A1&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A1-intro-to-rust&quot;&gt;Rust 101 -3: Memory and ownership slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 2: Language basics</title>
          <pubDate>Mon, 11 Sep 2023 07:33:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/09/11/rust-101-2-language-basics/</link>
          <guid>https://artificialworlds.net/blog/2023/09/11/rust-101-2-language-basics/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/09/11/rust-101-2-language-basics/">&lt;p&gt;An introduction to the Rust language basics.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 2: Language basics&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;87dae48e-64fc-416c-b219-2fb9f32b0bba&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;strong&gt;Language basics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Language basics): &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-1-course-intro&#x2F;&quot;&gt;1: Intro&lt;&#x2F;a&gt;, &lt;strong&gt;2: Language basics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-3-memory-and-ownership&#x2F;&quot;&gt;3: Memory and ownership&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-4-exercises-for-module-ai&#x2F;&quot;&gt;4: Exercises A1&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What Rust is and why you might want to learn it&lt;&#x2F;li&gt;
&lt;li&gt;Examining a simple program&lt;&#x2F;li&gt;
&lt;li&gt;Learning about the types of variable you can have (numbers, strings, tuples, arrays)&lt;&#x2F;li&gt;
&lt;li&gt;Introducing control flow with if, for, while and loop&lt;&#x2F;li&gt;
&lt;li&gt;Talking about functions and expressions&lt;&#x2F;li&gt;
&lt;li&gt;Preparing ourselves for the next video, which is about memory management&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you&#x27;d like to learn more about Unicode and character sets, try my video &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;w&#x2F;nxfiDc2t4v2dVzaB9UMaz6&quot;&gt;Interesting Characters&lt;&#x2F;a&gt; where I share how surprisingly interesting this whole area is.&lt;&#x2F;p&gt;
&lt;p&gt;Links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Florian Gilcher on &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;l8Qk5Nh6qsg&quot;&gt;Why Learn Rust?&lt;&#x2F;a&gt;&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;A1-intro-to-rust&quot;&gt;Rust 101 - 2: Language basics slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust 101 - 1: Course intro</title>
          <pubDate>Mon, 11 Sep 2023 07:30:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/09/11/rust-101-1-course-intro/</link>
          <guid>https://artificialworlds.net/blog/2023/09/11/rust-101-1-course-intro/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/09/11/rust-101-1-course-intro/">&lt;p&gt;Introducing the Rust 101 series and how to install Rust.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rust 101 - 1: Course intro&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;696a2c05-7ecd-4292-a270-459409eea548&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Series: &lt;strong&gt;Language basics&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-5-references&#x2F;&quot;&gt;More syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;02&#x2F;19&#x2F;rust-101-12-traits&#x2F;&quot;&gt;Traits and generics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;04&#x2F;11&#x2F;rust-101-18-dependencies-and-cargo-toml&#x2F;&quot;&gt;Building applications&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;05&#x2F;13&#x2F;rust-101-24-parallelism-and-rayon&#x2F;&quot;&gt;Concurrency and parallelism&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;06&#x2F;10&#x2F;rust-101-28-dynamic-dispatch&#x2F;&quot;&gt;Trait objects&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;07&#x2F;18&#x2F;rust-101-34-what-is-async&#x2F;&quot;&gt;Async&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;09&#x2F;06&#x2F;rust-101-42-why-do-we-need-unsafe&#x2F;&quot;&gt;Unsafe&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2024&#x2F;10&#x2F;23&#x2F;rust-101-49-foreign-function-interfaces&quot;&gt;FFI&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section (Language basics): &lt;strong&gt;1: Intro&lt;&#x2F;strong&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-2-language-basics&#x2F;&quot;&gt;2: Language basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;11&#x2F;rust-101-3-memory-and-ownership&#x2F;&quot;&gt;3: Memory and ownership&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;09&#x2F;30&#x2F;rust-101-4-exercises-for-module-ai&#x2F;&quot;&gt;4: Exercises A1&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Rust 101 is a series of videos explaining how to write programs in Rust.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustup.rs&#x2F;&quot;&gt;How to install Rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;0-intro&quot;&gt;Rust 101 - 1: Course intro slides&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Exercises: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;rust-101&#x2F;exercises&#x2F;A1-language-basics&#x2F;mod.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Follow the &quot;Exercises&quot; link to find the other tools you might want to install to follow along.&lt;&#x2F;p&gt;
&lt;p&gt;The course materials for this series are developed by tweede golf. You can find more information at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tweedegolf&#x2F;101-rs&quot;&gt;github.com&#x2F;tweedegolf&#x2F;101-rs&lt;&#x2F;a&gt; and you can sponsor the work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;sponsors&#x2F;tweedegolf&quot;&gt;github.com&#x2F;sponsors&#x2F;tweedegolf&lt;&#x2F;a&gt;. They are released under the Creative Commons Attribution Share Alike 4.0 International license.&lt;&#x2F;p&gt;
&lt;p&gt;This series of videos is copyright 2023 Andy Balaam and the tweede golf contributors and is released under the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;creativecommons.org&#x2F;licenses&#x2F;by-sa&#x2F;4.0&#x2F;&quot;&gt;Creative Commons Attribution Share Alike 4.0 International license&lt;&#x2F;a&gt;].&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Combining two function types with &amp; (ampersand) in TypeScript (intersection)</title>
          <pubDate>Fri, 25 Aug 2023 08:17:51 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/08/25/combining-two-function-types-with-ampersand-in-typescript-intersection/</link>
          <guid>https://artificialworlds.net/blog/2023/08/25/combining-two-function-types-with-ampersand-in-typescript-intersection/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/08/25/combining-two-function-types-with-ampersand-in-typescript-intersection/">&lt;h2 id=&quot;combining-interfaces-objects-with&quot;&gt;Combining interfaces&#x2F;objects with &amp;amp;&lt;&#x2F;h2&gt;
&lt;p&gt;When you combine two types in TypeScript with &amp;amp; (ampersand), it is called an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;objects.html#intersection-types&quot;&gt;Intersection Type&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;pre&gt;interface Particle {
    mass: number;
}

interface Wave {
    wavelength: number;
}

type Both = &lt;span style=&quot;color: red;&quot;&gt;Particle &amp;amp; Wave&lt;&#x2F;span&gt;;&lt;&#x2F;pre&gt;
&lt;p&gt;The new type Both is both a particle and a wave, so it has both properties mass and wavelength.&lt;&#x2F;p&gt;
&lt;p&gt;If you combined the types with |, making a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;2&#x2F;everyday-types.html#union-types&quot;&gt;Union Type&lt;&#x2F;a&gt;, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;type Either = &lt;span style=&quot;color: green;&quot;&gt;Particle | Wave&lt;&#x2F;span&gt;;&lt;&#x2F;pre&gt;
&lt;p&gt;then the new type Either would be either a particle or a wave, not both. To use it, you would need to find out which it was, before accessing only the property (mass or wavelength) that you now know exists, and not the other one.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;combining-functions-with&quot;&gt;Combining functions with &amp;amp;&lt;&#x2F;h2&gt;
&lt;p&gt;This is all fine, and relatively easy to understand, but what about when you combine function types in this way?&lt;&#x2F;p&gt;
&lt;pre&gt;type TakesString = (s: string) =&amp;gt; void;
type TakesNumber = (n: number) =&amp;gt; void;
type TakesStringOrNumber = &lt;span style=&quot;color: red;&quot;&gt;TakesString &amp;amp; TakesNumber&lt;&#x2F;span&gt;;&lt;&#x2F;pre&gt;
&lt;p&gt;Now you have a type that is both TakesString AND TakesNumber, which means a function that can do both things, which means it takes a string or a number as an argument. I can create something that has this type like this:&lt;&#x2F;p&gt;
&lt;pre&gt;const f: TakesStringOrNumber = (x: &lt;span style=&quot;color: green;&quot;&gt;string | number&lt;&#x2F;span&gt;) =&amp;gt; {console.log(x);};&lt;&#x2F;pre&gt;
&lt;p&gt;Notice how that | symbol sneaked in there? In order to satisfy the &amp;amp; on the function types, the argument types use |. When two things have this kind of opposite relationship it&#x27;s sometimes known as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Covariance_and_contravariance&quot;&gt;contravariance&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It has to be this way: for the function to be TakesString AND TakesNumber the argument needs to be string OR number.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;functions-with-literal-types&quot;&gt;Functions with literal types&lt;&#x2F;h2&gt;
&lt;p&gt;If your functions take &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.typescriptlang.org&#x2F;docs&#x2F;handbook&#x2F;literal-types.html&quot;&gt;Literal Types&lt;&#x2F;a&gt; this can get even more confusing:&lt;&#x2F;p&gt;
&lt;pre&gt;type TakesTrue = (success: &lt;span style=&quot;color: blue;&quot;&gt;true&lt;&#x2F;span&gt;) =&amp;gt; void;
type TakesFalse = (success: &lt;span style=&quot;color: blue;&quot;&gt;false&lt;&#x2F;span&gt;) =&amp;gt; void;&lt;&#x2F;pre&gt;
&lt;p&gt;Any argument you pass to a function that is TakesTrue must be true. Similarly, to call a function that is TakesFalse you must pass in false. TypeScript lets you do this, which is fun.&lt;&#x2F;p&gt;
&lt;p&gt;So now imagine you combine these types:&lt;&#x2F;p&gt;
&lt;pre&gt;type TakesEither = &lt;span style=&quot;color: red;&quot;&gt;TakesTrue &amp;amp; TakesFalse&lt;&#x2F;span&gt;;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, TakesEither is both TakesTrue and TakesFalse so it can take in either true or false.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s make a function that can do that:&lt;&#x2F;p&gt;
&lt;pre&gt;const f: TakesEither = (success: boolean) =&amp;gt; { console.log(`${success}`); };&lt;&#x2F;pre&gt;
&lt;p&gt;This works - f takes a boolean, so it does indeed allow you to pass in true or false, as required to be a TakesEither.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s weird though, is that you can&#x27;t do this:&lt;&#x2F;p&gt;
&lt;pre&gt;let x: boolean = new Date().getHours &amp;gt; 12;
f(x); &#x2F;&#x2F; Compile error&lt;&#x2F;pre&gt;
&lt;p&gt;(Ignore the date stuff - x is just a boolean that might be true or false.)&lt;&#x2F;p&gt;
&lt;p&gt;Here is the compile error:&lt;&#x2F;p&gt;
&lt;pre&gt;index.ts:9:7 - error TS2769: No overload matches this call.
  Overload 1 of 2, &#x27;(success: true): void&#x27;, gave the following error.
    Argument of type &#x27;boolean&#x27; is not assignable to parameter of type &#x27;true&#x27;.
  Overload 2 of 2, &#x27;(success: false): void&#x27;, gave the following error.
    Argument of type &#x27;boolean&#x27; is not assignable to parameter of type &#x27;false&#x27;.&lt;&#x2F;pre&gt;
&lt;p&gt;The thing is that the compiler only knows that f is a TakesEither, which means that it is either a function that takes true or a function that takes false. It doesn&#x27;t know that f can take a boolean (even though here it can!).&lt;&#x2F;p&gt;
&lt;p&gt;This code does work:&lt;&#x2F;p&gt;
&lt;pre&gt;let x: boolean = new Date().getHours() &amp;gt; 12;
if (x) {
    f(x); &#x2F;&#x2F; OK
} else {
    f(x); &#x2F;&#x2F; OK
}&lt;&#x2F;pre&gt;
&lt;p&gt;Why? Because inside the first part of the if, the compiler knows that x is true, so it can call f with it, because f can take true as an argument. Similarly in the second half, it knows x is false.&lt;&#x2F;p&gt;
&lt;p&gt;In future, the compiler might &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;TypeScript&#x2F;issues&#x2F;14107&quot;&gt;grow the ability to handle work in the first case&lt;&#x2F;a&gt;, but at time of writing, we see this error.&lt;&#x2F;p&gt;
&lt;p&gt;Cool, huh?&lt;&#x2F;p&gt;
&lt;p&gt;(Thanks to jcalz for this stackoverflow answer that helped me understand this a little: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;a&#x2F;66495246&quot;&gt;typescript intersection types of function&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Accessing services on the host from a Docker container or a Podman one</title>
          <pubDate>Fri, 18 Aug 2023 11:39:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/08/18/accessing-services-on-the-host-from-a-docker-container-or-a-podman-one/</link>
          <guid>https://artificialworlds.net/blog/2023/08/18/accessing-services-on-the-host-from-a-docker-container-or-a-podman-one/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/08/18/accessing-services-on-the-host-from-a-docker-container-or-a-podman-one/">&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;podman.io&#x2F;&quot;&gt;Podman&lt;&#x2F;a&gt; to provide Docker-like stuff on my dev machine without &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;research.nccgroup.com&#x2F;2016&#x2F;02&#x2F;06&#x2F;abusing-privileged-and-unprivileged-linux-containers&#x2F;&quot;&gt;effectively being root&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;talking-to-the-container-host-in-docker&quot;&gt;Talking to the container host in Docker&lt;&#x2F;h3&gt;
&lt;p&gt;There is a little trick for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.howtogeek.com&#x2F;devops&#x2F;how-to-connect-to-localhost-within-a-docker-container&#x2F;&quot;&gt;accessing HTTP services on the container host in Docker&lt;&#x2F;a&gt;: you add --add-host host.docker.internal:host-gateway to the command line when you run the docker container, for example:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run -d &lt;b&gt;--add-host host.docker.internal:host-gateway&lt;&#x2F;b&gt; my-container:latest
&lt;&#x2F;pre&gt;
&lt;p&gt;Now in your code you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;for-linux&#x2F;issues&#x2F;264&quot;&gt;refer to the Docker container host&lt;&#x2F;a&gt; using the name host.docker.internal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;talking-to-the-container-host-in-podman&quot;&gt;Talking to the container host in Podman&lt;&#x2F;h3&gt;
&lt;p&gt;However, this doesn&#x27;t work in Podman. Instead, you &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.podman.io&#x2F;en&#x2F;latest&#x2F;markdown&#x2F;podman-run.1.html#network-mode-net&quot;&gt;need to&lt;&#x2F;a&gt; add --network slirp4netns:allow_host_loopback=true to the command line, for example:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run -d &lt;b&gt;--network slirp4netns:allow_host_loopback=true&lt;&#x2F;b&gt; my-container:latest
&lt;&#x2F;pre&gt;
&lt;p&gt;Now in your code you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;containers&#x2F;podman&#x2F;issues&#x2F;5651&quot;&gt;refer to the Podman container host&lt;&#x2F;a&gt; using the name host.containers.internal.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;supporting-both&quot;&gt;Supporting both&lt;&#x2F;h3&gt;
&lt;p&gt;For this to work in both Docker and Podman, your container needs to know which framework it is working inside, and use either host.docker.internal or host.containers.internal explicitly, which is a pity.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why I&#x27;m voting tactically against you - letter to my Conservative MP</title>
          <pubDate>Thu, 06 Jul 2023 07:38:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/07/06/why-im-voting-tactically-against-you-letter-to-my-conservative-mp/</link>
          <guid>https://artificialworlds.net/blog/2023/07/06/why-im-voting-tactically-against-you-letter-to-my-conservative-mp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/07/06/why-im-voting-tactically-against-you-letter-to-my-conservative-mp/">&lt;p&gt;Here&#x27;s the letter I just sent to my MP. Feel free to use it in full or in part.&lt;&#x2F;p&gt;
&lt;p&gt;Dear Ben Wallace,&lt;&#x2F;p&gt;
&lt;p&gt;Last night I made the decision to vote tactically against you at the next election, and I felt I owed you an explanation.&lt;&#x2F;p&gt;
&lt;p&gt;I now feel that any of the likely alternatives is a better option than a continuation of your government. I have four main reasons.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-refugee-crisis&quot;&gt;1. Refugee Crisis&lt;&#x2F;h3&gt;
&lt;p&gt;Our government&#x27;s catastrophic policy of deporting asylum seekers to Rwanda is immoral, illegal and, I hope and believe, impossible to achieve.&lt;&#x2F;p&gt;
&lt;p&gt;It is destroying our international reputation.&lt;&#x2F;p&gt;
&lt;p&gt;Whenever you get a chance to vote against Suella Braverman or anyone else from the hateful wing of your party, please take it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-climate-crisis&quot;&gt;2. Climate Crisis&lt;&#x2F;h3&gt;
&lt;p&gt;Our government is asleep at the wheel. Carbon reduction should be at the centre of every part of government policy, and wholesale energy transition should be the most important consideration for our budget and policy.&lt;&#x2F;p&gt;
&lt;p&gt;In order for our world as we know it to survive, we need to act now. Further, if the UK acts quickly it still has the chance to be a world leader and benefit financially from the opportunities offered by new technology.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, we are trailing behind on our carbon commitments, and failing to meet our promises on help for countries suffering the early effects.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-nhs-crisis&quot;&gt;3. NHS Crisis&lt;&#x2F;h3&gt;
&lt;p&gt;There are not enough staff in the NHS and those that remain are worn out. As a former doctor I&#x27;m sure I don&#x27;t need to explain the situation to you.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m sure reform is needed, but the most urgent need is for money. I want to pay more tax to invest in a future where my healthcare is secure. I don&#x27;t want to live in a society where the risk of losing my job or becoming long-term sick makes me unable to access healthcare. I take huge comfort from the knowledge that the NHS has my back, and your government appears to be actively working to take that away.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-international-aid-cuts&quot;&gt;4. International Aid Cuts&lt;&#x2F;h3&gt;
&lt;p&gt;The enormous cut in our international aid budget (notwithstanding the pathetic attempt to mask it by redirecting funds towards domestic Home Office spending on the refugee crisis) is disastrous for millions of people around the world, and takes away one of our most effective tools for preventing refugees from needing to escape their home countries.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m afraid there is little you can do to prevent any of the above, but I wish you well trying, and for picking up the pieces after the landslide defeat I hope and trust is on its way. I encourage you to try and re-enter the centre ground of politics rather than sliding further towards the hateful wing.&lt;&#x2F;p&gt;
&lt;p&gt;Yours,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rust WASM hello world - no need for webpack!</title>
          <pubDate>Tue, 27 Jun 2023 20:15:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/06/27/rust-wasm-hello-world-no-need-for-webpack/</link>
          <guid>https://artificialworlds.net/blog/2023/06/27/rust-wasm-hello-world-no-need-for-webpack/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/06/27/rust-wasm-hello-world-no-need-for-webpack/">&lt;p&gt;Up to now I&#x27;ve been following the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustwasm.github.io&#x2F;docs&#x2F;book&#x2F;game-of-life&#x2F;hello-world.html&quot;&gt;official guide&lt;&#x2F;a&gt; and using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;&quot;&gt;webpack&lt;&#x2F;a&gt; to package my Rust+WASM code to run in a browser.&lt;&#x2F;p&gt;
&lt;p&gt;But today I found out there is no need for webpack at all! This makes development much faster, with many fewer dependencies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;p&gt;Before you start:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;tools&#x2F;install&quot;&gt;Install Rust&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustwasm.github.io&#x2F;wasm-pack&#x2F;installer&#x2F;&quot;&gt;Install wasm-pack&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;create-a-project&quot;&gt;Create a project&lt;&#x2F;h2&gt;
&lt;pre&gt;cargo new --lib smol-but-rusty&lt;&#x2F;pre&gt;
&lt;p&gt;This will create a new project. Edit its Cargo.toml to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;[package]
name = &quot;smol-but-rusty&quot;
version = &quot;0.1.0&quot;
edition = &quot;2021&quot;

&lt;span style=&quot;color: #090;&quot;&gt;[lib]&lt;&#x2F;span&gt;
&lt;span style=&quot;color: #090;&quot;&gt;crate-type = [&quot;cdylib&quot;, &quot;rlib&quot;]&lt;&#x2F;span&gt;

[dependencies]
&lt;span style=&quot;color: #090;&quot;&gt;wasm-bindgen = &quot;0.2.63&quot;&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;write-your-code&quot;&gt;Write your code&lt;&#x2F;h2&gt;
&lt;p&gt;Change src&#x2F;lib.rs to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: #090;&quot;&gt;use wasm_bindgen::prelude::*;&lt;&#x2F;span&gt;

&lt;span style=&quot;color: #090;&quot;&gt;#[wasm_bindgen]&lt;&#x2F;span&gt;
pub fn say_hello() -&amp;gt; String {
    &quot;Hello, world!&quot;.to_owned()
}&lt;&#x2F;pre&gt;
&lt;p&gt;Any function you want to call from JavaScript needs to be annotated like this. (Note: there are lots of restrictions on what types you can accept and return, to make the WASM compatible with JavaScript.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;build-it&quot;&gt;Build it&lt;&#x2F;h2&gt;
&lt;pre&gt;cd smol-but-rusty
cargo build
&lt;span style=&quot;color: #090;&quot;&gt;wasm-pack build --target=web&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will create a pkg directory containing your compiled WASM and wrappers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;refer-to-it-from-html&quot;&gt;Refer to it from HTML&lt;&#x2F;h2&gt;
&lt;p&gt;Create a new directory called www. Inside, create a file index.html and make it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script &lt;span style=&quot;color: #090;&quot;&gt;type=&quot;module&quot;&lt;&#x2F;span&gt;&amp;gt;
      &lt;span style=&quot;color: #090;&quot;&gt;import init, { say_hello } from &#x27;.&#x2F;smol_but_rusty.js&#x27;;&lt;&#x2F;span&gt;

      &lt;span style=&quot;color: #090;&quot;&gt;async function run()&lt;&#x2F;span&gt; {
        &lt;span style=&quot;color: #090;&quot;&gt;await init();&lt;&#x2F;span&gt;
        const p = document.createElement(&quot;p&quot;);
        p.innerText = &lt;span style=&quot;color: #090;&quot;&gt;say_hello();&lt;&#x2F;span&gt;
        document.body.appendChild(p);
      }

      &lt;span style=&quot;color: #090;&quot;&gt;run();&lt;&#x2F;span&gt;
    &amp;lt;&#x2F;script&amp;gt;
  &amp;lt;&#x2F;body&amp;gt;
&amp;lt;&#x2F;html&amp;gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code that calls say_hello is calling the Rust function you wrote from JavaScript. It has been compiled into WASM in pkg&#x2F;smol_but_rusty_bg.wasm, and wrapped by some JavaScript in pkg&#x2F;smol_but_rusty.js.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;combine-wasm-with-html&quot;&gt;Combine WASM with HTML&lt;&#x2F;h2&gt;
&lt;p&gt;We placed index.html outside pkg because pkg is for generated stuff, and index.html is hand-coded.&lt;&#x2F;p&gt;
&lt;p&gt;Now we combine them together with:&lt;&#x2F;p&gt;
&lt;pre&gt;cp www&#x2F;* pkg&#x2F;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;serve-it&quot;&gt;Serve it&lt;&#x2F;h2&gt;
&lt;p&gt;To see this in a web browser, serve it through a web server. For example, if you&#x27;ve got Python3:&lt;&#x2F;p&gt;
&lt;pre&gt;cd pkg
python3 -m http.server&lt;&#x2F;pre&gt;
&lt;p&gt;Now visit &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;0.0.0.0:8000&#x2F;&quot;&gt;http:&#x2F;&#x2F;0.0.0.0:8000&#x2F;&lt;&#x2F;a&gt; and if you&#x27;re lucky you&#x27;ll see this:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wasm-hello.png&quot; alt=&quot;&quot; width=&quot;464&quot; height=&quot;193&quot; class=&quot;aligncenter size-full wp-image-3602&quot;&gt;
&lt;p&gt;If not, check the steps above looking out for any error messages, and press F12 in your browser and click Console to see any errors in there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-webpack&quot;&gt;No webpack!&lt;&#x2F;h2&gt;
&lt;p&gt;No webpacks were used in the making of this code. Just Rust compiled to WASM and imported into a modern web browser.&lt;&#x2F;p&gt;
&lt;p&gt;You can write tests as normal and run them with cargo test. You can also run your tests in the browser(!) with wasm-pack test --firefox --headless if you have wasm-bindgen-test = &quot;0.3.13&quot; in your [dev-dependencies] in Cargo.toml.&lt;&#x2F;p&gt;
&lt;p&gt;You can write any Rust code you like, so long as the functions annotated with #[wasm_bindgen] work the way they need to for JavaScript compatibility.&lt;&#x2F;p&gt;
&lt;p&gt;You can also add lots of pure-Rust dependencies in Cargo.toml. Most things will compile without problems to WASM, and if they don&#x27;t, sometimes you can enable a feature that makes them work.&lt;&#x2F;p&gt;
&lt;p&gt;You probably want to update your Cargo.toml to include some of the good stuff in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rustwasm&#x2F;wasm-pack-template&#x2F;blob&#x2F;master&#x2F;Cargo.toml&quot;&gt;example Cargo.toml&lt;&#x2F;a&gt;, including optimising for size (opt-level = &quot;s&quot;), setting up a custom console_error_panic_hook to make the errors better, and maybe even the smaller allocator wee_alloc.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Estimating software tasks and stories: avoid time-based estimates</title>
          <pubDate>Wed, 07 Jun 2023 08:54:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/06/07/estimating-software-tasks-and-stories-avoid-time-based-estimates/</link>
          <guid>https://artificialworlds.net/blog/2023/06/07/estimating-software-tasks-and-stories-avoid-time-based-estimates/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/06/07/estimating-software-tasks-and-stories-avoid-time-based-estimates/">&lt;p&gt;I was recently asked what I thought of using time-based estimates when tracking software tasks.&lt;&#x2F;p&gt;
&lt;p&gt;TLDR: &lt;strong&gt;I think it&#x27;s a terrible idea that can hurt your software by introducing bugs and burning out developers.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I think using time-based estimates is pretty toxic. It leads to people being &quot;held accountable&quot; i.e. pressured and&#x2F;or expected to work long hours to meet the &quot;estimate&quot;, which magically morphs into a &lt;strong&gt;target&lt;&#x2F;strong&gt;. This makes developers rush, causing bugs and missed tests and hygiene tasks, which ALWAYS come back to bite you, and always earlier than you expected. It also makes developers stressed and tired, which makes them write worse code.&lt;&#x2F;p&gt;
&lt;p&gt;These problems are almost impossible to avoid, even if the manager deliberately never mentions estimates! It&#x27;s just human nature to feel you should stick to your estimate.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, I advocate &lt;strong&gt;relative units&lt;&#x2F;strong&gt; (e.g. &quot;story points&quot;). These can be used for prediction because you can look at past work to see how long an average story point takes, but they don&#x27;t come with the baggage that your prediction will be spot on.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: to bootstrap this process, you will need to compare new work with things you have already done. Get a feel for how complex an old task was, and choose an arbitrary amount for that task. Now estimate new tasks by saying e.g. &quot;that feels about twice as complex as X&quot;. This also allows you to bootstrap your prediction process because you can see how long that old task took to complete.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In either case, I also advocate for a &lt;strong&gt;ranged estimate&lt;&#x2F;strong&gt; (low-high). When predicting, I always take the high.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;It&#x27;s also really important to work as a team to estimate, not do it individually.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Another rule: &lt;strong&gt;only the people who implement the task do the estimates&lt;&#x2F;strong&gt;. This prevents two antipatterns: 1. someone who has no skin in the game thinking it&#x27;s going to be easy without really engaging with the problem, and 2. a manager or person in authority who wants to try and make it go faster by estimating low. This NEVER works, and instead makes your developers stressed.&lt;&#x2F;p&gt;
&lt;p&gt;If there is disagreement or the range is wide, either just make it high (if the story is not going to be done soon), or take the time to figure out why people disagree or are uncertain. This always pays off, and it means taking the high end when you predict doesn&#x27;t result in inflated estimates.&lt;&#x2F;p&gt;
&lt;p&gt;Further reading: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.martinfowler.com&#x2F;bliki&#x2F;PurposeOfEstimation.html&quot;&gt;Martin Fowler: Purpose of Estimation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Maths: The Fun Parts - Graphs video</title>
          <pubDate>Thu, 25 May 2023 08:26:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/05/25/maths-the-fun-parts-graphs-video/</link>
          <guid>https://artificialworlds.net/blog/2023/05/25/maths-the-fun-parts-graphs-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/05/25/maths-the-fun-parts-graphs-video/">&lt;p&gt;More of my series about my favourite bits of Maths. This time, Graphs:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Maths: The Fun Parts - Graphs&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;98b258e4-de13-43a2-8850-dcd734aaddcd&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;maths-the-fun-parts&#x2F;maths-the-fun-parts.html&quot;&gt;Slides for Maths: The Fun Parts - Graphs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Maths: The Fun Parts - Groups video</title>
          <pubDate>Thu, 18 May 2023 08:09:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/05/18/maths-the-fun-parts-groups-video/</link>
          <guid>https://artificialworlds.net/blog/2023/05/18/maths-the-fun-parts-groups-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/05/18/maths-the-fun-parts-groups-video/">&lt;p&gt;More of my series about my favourite bits of Maths. This time, Groups:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Maths: The Fun Parts - Groups&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e0760880-f712-4480-b5a9-5aa560fca3c5&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;maths-the-fun-parts&#x2F;maths-the-fun-parts.html&quot;&gt;Slides for Maths: The Fun Parts - Groups&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Maths: The Fun Parts - Sets video</title>
          <pubDate>Thu, 11 May 2023 08:03:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/05/11/maths-the-fun-parts-sets-video/</link>
          <guid>https://artificialworlds.net/blog/2023/05/11/maths-the-fun-parts-sets-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/05/11/maths-the-fun-parts-sets-video/">&lt;p&gt;I&#x27;ve been wanting to make series about the cool bits of Maths for years and I&#x27;m finally getting around to it.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s called &quot;Maths: The Fun Parts&quot; and the first part is on Sets:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Maths: The Fun Parts - Sets&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;28ce4a34-ad04-4314-a644-40bdcae1bc3e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;maths-the-fun-parts&#x2F;maths-the-fun-parts.html&quot;&gt;Slides for Maths: The Fun Parts - Sets&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>GitHub API GraphQL snippets</title>
          <pubDate>Fri, 05 May 2023 09:29:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/05/05/github-api-graphql-snippets/</link>
          <guid>https://artificialworlds.net/blog/2023/05/05/github-api-graphql-snippets/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/05/05/github-api-graphql-snippets/">&lt;p&gt;Every time I try to use GitHib&#x27;s GraphQL API I find myself totally lost, so here are some snippets I have found useful.&lt;&#x2F;p&gt;
&lt;p&gt;All of these can be pasted into the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;graphql&#x2F;overview&#x2F;explorer&quot;&gt;GitHub GraphQL API Explorer&lt;&#x2F;a&gt;, which can run the code as your logged-in user in the browser without the need to go through an authentication dance.&lt;&#x2F;p&gt;
&lt;p&gt;I will add more over time, and feel free to add any you like in the comments.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;list-all-the-projects-in-an-organisation&quot;&gt;List all the projects in an organisation&lt;&#x2F;h2&gt;
&lt;pre&gt;query{
    organization(login: &quot;matrix-org&quot;) {
      projectsV2(first: 20) {
        nodes {
          id
          title
        }
      }
    }
}&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Live code reviews make life better</title>
          <pubDate>Fri, 24 Mar 2023 12:54:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2023/03/24/live-code-reviews-make-life-better/</link>
          <guid>https://artificialworlds.net/blog/2023/03/24/live-code-reviews-make-life-better/</guid>
          <description xml:base="https://artificialworlds.net/blog/2023/03/24/live-code-reviews-make-life-better/">&lt;p&gt;I&#x27;ve just got off a call with a colleague. During that call I:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;learned a lot about how the work he is doing fits in with what my team is working on&lt;&#x2F;li&gt;
&lt;li&gt;understood the specific code we were discussing much better than if I&#x27;d looked at it alone&lt;&#x2F;li&gt;
&lt;li&gt;helped him find a whole bit of code we didn&#x27;t actually need (the best kind of code)!&lt;&#x2F;li&gt;
&lt;li&gt;helped him figure out something else he has been musing about, that was mostly unrelated&lt;&#x2F;li&gt;
&lt;li&gt;had a nice chat and felt much better about life&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Asynchronous code reviews can feel like a duty I must perform, and one that distracts me from what I&#x27;m doing. Meanwhile, when receiving such reviews, if we&#x27;re not careful it can feel like someone criticising for criticism&#x27;s sake, or just making your work take longer. As an offline reviewer I don&#x27;t often remember to say what I like as well as what I&#x27;d want to change, and I normally feel a push to suggest at least one change just to prove I read the code.&lt;&#x2F;p&gt;
&lt;p&gt;Live reviews are so much better! They:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;are full of opportunities to learn, for both people&lt;&#x2F;li&gt;
&lt;li&gt;help you understand the code so you can do a better review&lt;&#x2F;li&gt;
&lt;li&gt;often lead the person who wrote the code to spot improvements that the reviewer had no chance of seeing&lt;&#x2F;li&gt;
&lt;li&gt;don&#x27;t have to be the whole review: you can still take your time to think carefully after the discussion, before clicking Submit&lt;&#x2F;li&gt;
&lt;li&gt;can be fun, and give you a chance to interact with your colleagues&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I much prefer them. I&#x27;m going to do more.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Air-Source Heat Pump - 1 year later</title>
          <pubDate>Thu, 24 Nov 2022 09:37:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/11/24/air-source-heat-pump-1-year-later/</link>
          <guid>https://artificialworlds.net/blog/2022/11/24/air-source-heat-pump-1-year-later/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/11/24/air-source-heat-pump-1-year-later/">&lt;p&gt;10 months ago I wrote a blog post &lt;a href=&quot;&#x2F;blog&#x2F;2022&#x2F;01&#x2F;09&#x2F;air-source-heat-pump-our-experience-so-far-2-months-in&#x2F;&quot;&gt;Air-Source Heat Pump – our experience so far, 2 months in&lt;&#x2F;a&gt; about our new air source heat pump. Have a look back at that for photos of the device itself and more detail about installation etc.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;less-energy&quot;&gt;Less energy&lt;&#x2F;h2&gt;
&lt;p&gt;We used a lot less energy this year than last year. Here&#x27;s the graph for 2 years:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;energy-per-day.png&quot; alt=&quot;Graph showing 2 years of energy usage on gas and electricity. Gas usage stops halfway through because a heat pump was installed. The second year&#x27;s gas usage is much lower than the first, especially during the heavy use, cold months.&quot; width=&quot;720&quot; height=&quot;424&quot; class=&quot;aligncenter size-full wp-image-3532&quot;&gt;
&lt;p&gt;As you can see, we used a lot less energy in kWh this year than last year. Air source heat pumps work!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more-money&quot;&gt;More money&lt;&#x2F;h2&gt;
&lt;p&gt;Our energy cost more this year than last year. I&#x27;ve calculated this graph based on fixed prices, and I used 2021 prices to keep consistency with the last blog post, but the real-prices story is similar. Here is the graph for the last 2 years:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;cost-per-day.png&quot; alt=&quot;Graph showing energy cost per day over 2 years. The first half shows gas usage, which drops to zero in the middle when a heat pump was installed. The second half is higher, showing that the cost of the electricity this year was more than last year. Towards the end, solar panels were installed and the cost drops below last year.&quot; width=&quot;720&quot; height=&quot;424&quot; class=&quot;aligncenter size-full wp-image-3534&quot;&gt;
&lt;p&gt;Why did our cost go up when our energy usage went down so dramatically?&lt;&#x2F;p&gt;
&lt;p&gt;Because electricity is too expensive!&lt;&#x2F;p&gt;
&lt;p&gt;Electricity is the right way to power our cars and homes, because it can be sourced sustainably, and in fact much of it &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nationalgrid.com&#x2F;stories&#x2F;energy-explained&#x2F;how-much-uks-energy-renewable&quot;&gt;really is being sourced sustainably right now&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Artificially-high electricity prices are preventing people switching to a better way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solar-panels-help&quot;&gt;Solar panels help!&lt;&#x2F;h2&gt;
&lt;p&gt;In September this year we had solar panels installed. They look great and are working incredibly well. We were getting 10kWh per day from them in September. We don&#x27;t have a battery yet, but when it arrives we think we will be able to cover most of our summer energy costs using these panels.&lt;&#x2F;p&gt;
&lt;p&gt;Since the panels were installed, our reduced use of grid electricity meant that our energy costs for this year dropped below last year. Obviously it&#x27;s too soon to say for sure what the full impact is, but I can say we are very happy with our solar panels.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;our-house-is-cozy&quot;&gt;Our house is cozy&lt;&#x2F;h2&gt;
&lt;p&gt;Our new heat pump heats our leaky house very well, and we are nice and cozy, even when temperatures outside drop below zero. The heat pump is less efficient when it&#x27;s cold outside, but still way better than a gas boiler.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;installed-by-your-energy-your-way&quot;&gt;Installed by Your Energy Your Way&lt;&#x2F;h2&gt;
&lt;p&gt;[My wife used to be director of the company, so I declare an interest.]&lt;&#x2F;p&gt;
&lt;p&gt;Our heat pump, radiators and panels were installed by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.yourenergyyourway.co.uk&#x2F;&quot;&gt;Your Energy Your Way&lt;&#x2F;a&gt; and I can recommend them for good communication, service and quality.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Deleted my Twitter account</title>
          <pubDate>Sun, 20 Nov 2022 20:06:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/11/20/deleted-my-twitter-account/</link>
          <guid>https://artificialworlds.net/blog/2022/11/20/deleted-my-twitter-account/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/11/20/deleted-my-twitter-account/">&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: my twitter archive is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;tweets&#x2F;&quot;&gt;artificialworlds.net&#x2F;tweets&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: you can see the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;twitter-archive-parser&quot;&gt;hacky scripts&lt;&#x2F;a&gt; I used to build this, based on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;timhutton&#x2F;twitter-archive-parser&quot;&gt;twitter-archive-parser&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This evening I deleted my Twitter account. I&#x27;m feeling surprisingly unsettled by doing it, to be honest.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;twitter-deactivated.png&quot; alt=&quot;The Twitter mobile interface showing that my account has been deactivated.&quot; width=&quot;720&quot; height=&quot;352&quot; class=&quot;aligncenter size-full wp-image-3515&quot;&gt;
&lt;p&gt;I can&#x27;t participate in a platform that gives a voice to people who incite violence. I said I would leave if Trump were re-instated, and that is what I am doing. I know Twitter has failed in the past in many ways, but this deliberate decision is the last straw.&lt;&#x2F;p&gt;
&lt;p&gt;I have downloaded the archive of my tweets, and I will process it and upload it to my web site as soon as possible.&lt;&#x2F;p&gt;
&lt;p&gt;You can find me at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like help finding your way onto Mastodon, feel free to ask me for help via Mastodon itself, or by email on andybalaam at artificialworlds.net&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>IETF115 Trip Report (Can Matrix help messaging standardisation through MIMI?)</title>
          <pubDate>Wed, 09 Nov 2022 16:06:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/11/09/ietf115-trip-report-can-matrix-help-messaging-standardisation-through-mimi/</link>
          <guid>https://artificialworlds.net/blog/2022/11/09/ietf115-trip-report-can-matrix-help-messaging-standardisation-through-mimi/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/11/09/ietf115-trip-report-can-matrix-help-messaging-standardisation-through-mimi/">&lt;p&gt;Geeks don&#x27;t like to be formal, but we do like to be precise. That is the
contrast that comes to mind as I attend my first IETF meeting, IETF 115 in
London in November 2022.&lt;&#x2F;p&gt;
&lt;p&gt;Like most standards bodies, IETF appears to have been formed as a reaction to
stuffy, process-encumbered standards bodies. The introductory material
emphasises a rough-consensus style, with an emphasis on getting things done
and avoiding being a talking shop. This is backed up by the hackathon organised
on the weekend before the conference, allowing attendees to get a taste of
really doing stuff, writing code to solve those thorny problems.&lt;&#x2F;p&gt;
&lt;p&gt;But the IETF has power, and, rightly, with power come checks and balances, so
although there is no formal secretary for the meeting I was in, a volunteer was
found to take minutes, and they made sure to store them in the official system.
It really matters what happens here, and the motley collection of (mostly
slightly aging) participants know that.&lt;&#x2F;p&gt;
&lt;p&gt;There is an excitement about coming together to solve technical problems, but
there is also an understandable caution about the various agendas being
represented.&lt;&#x2F;p&gt;
&lt;p&gt;We were at the meeting to discuss something incredibly exciting: finding a
practical solution for how huge messaging providers (e.g. WhatsApp) can make
their systems &quot;interoperable&quot;. In other words, making it possible for people
using one instant messenger to talk to people using another one.&lt;&#x2F;p&gt;
&lt;p&gt;Because of the EU&#x27;s new Digital Markets Act (DMA), the largest messaging
providers are going to be obliged to provide ways for outside systems to link
into them, and the intention of the meeting I went to (&quot;More Instant Messaging
Interoperability&quot;, codenamed MIMI) is to agree on a way they can link together
that makes the experience good for users.&lt;&#x2F;p&gt;
&lt;p&gt;The hardest part of this is preserving end-to-end encryption across different
instant messengers. If the content of a message is encrypted, then you can&#x27;t
have a translation layer (&quot;bridge&quot;, in Matrix terminology) that converts those
contents from one format into another, because the translator would need to
decrypt the message first. Instead, either the client (the app on the user&#x27;s
device) needs to understand all possible formats, or we need to agree on a
common format.&lt;&#x2F;p&gt;
&lt;p&gt;The meeting was very interesting, and conversation on the day revolved around
what exactly should be in scope or out of scope for the group that will
continue this work. In particular, we discussed whether the group should work
on questions of how to identify and find people on other messaging networks,
and to what extent the problems of spam and abuse should be considered. Mostly
the conclusion was that the &quot;charter&quot; of the &quot;working group&quot;, when&#x2F;if it
is formed, should leave these topics open, so that the group can decide the
details when it is up and running.&lt;&#x2F;p&gt;
&lt;p&gt;From a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&quot;&gt;Matrix&lt;&#x2F;a&gt; point of view, this is very
exciting, because we have been working on exactly how to do this stuff for
years, and our goal from the beginning has been to allow interoperability
between other systems. So we have a lot to offer on how to design a system that
allows rich interactions to work between very different systems, while
providing effective systems to fight abuse. One part that we can&#x27;t tackle
without the co-operation of the dominant messengers is end-to-end encryption,
because we can&#x27;t control the message formats that are used on the clients, so
it&#x27;s really exciting that MIMI&#x27;s work might improve that situation.&lt;&#x2F;p&gt;
&lt;p&gt;I personally feel a little skeptical that the &quot;gatekeepers&quot; will implement DMA
in a really good way, like what we were discussing at IETF. I think it&#x27;s more
likely that they will do the bare minimum, and make it difficult to use so
that the experience is so bad that no-one uses it. However, if one or two major
players do adopt a proper interoperable standard, it could pick up all the
minor players, and become something genuinely useful.&lt;&#x2F;p&gt;
&lt;p&gt;I really enjoyed going to the IETF meeting, and am incredibly grateful to the
dedicated people doing this work. I really hope that the MIMI group forms and
successfully creates a useful standard. I&#x27;m also really optimistic that the
things we&#x27;ve learned while creating Matrix can help with this process.&lt;&#x2F;p&gt;
&lt;p&gt;Whether this standard begins to solve the horrible problems we have with closed
messaging silos and user lock-in to potentially exploitative or harmful
platforms is probably out of our hands.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to get involved with MIMI, check out the page here:&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;datatracker.ietf.org&#x2F;group&#x2F;mimi&#x2F;about&#x2F;&quot;&gt;datatracker.ietf.org&#x2F;group&#x2F;mimi&#x2F;about&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting the text selection in a browser: just use setBaseAndExtent</title>
          <pubDate>Thu, 29 Sep 2022 13:53:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/09/29/setting-the-text-selection-in-a-browser-just-use-setbaseandextent/</link>
          <guid>https://artificialworlds.net/blog/2022/09/29/setting-the-text-selection-in-a-browser-just-use-setbaseandextent/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/09/29/setting-the-text-selection-in-a-browser-just-use-setbaseandextent/">&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;Selection&quot;&gt;Selection&lt;&#x2F;a&gt; API is confusing and weird.&lt;&#x2F;p&gt;
&lt;p&gt;But, here&#x27;s what I&#x27;ve discovered: just use setBaseAndExtend, and when (rarely) needed, extend.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;Every selection in a browser consists of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;an &quot;anchor&quot; - the beginning, where you started dragging, and&lt;&#x2F;li&gt;
&lt;li&gt;a &quot;focus&quot; - the end, where your stopped dragging, and where your cursor is&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To select some text in a browser, find the from and to DOM nodes you want, and how far through them you want to be, and set the selection like this:&lt;&#x2F;p&gt;
&lt;pre&gt;const sel = document.getSelection();
sel.setBaseAndExtent(from_node, from_offset, to_node, to_offset);
&lt;&#x2F;pre&gt;
&lt;p&gt;If you want just a cursor with nothing selected, use the same node and offset for both from and to.&lt;&#x2F;p&gt;
&lt;p&gt;The Internet has a lot of recipes that involve creating Ranges and adding them to the Selection, but there is no need to do that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finding-the-from-and-to-nodes&quot;&gt;Finding the from and to nodes&lt;&#x2F;h2&gt;
&lt;p&gt;When the locations are inside a text node in the DOM, it&#x27;s easy to find the from and to nodes - they are just the text nodes, and the offsets are how many UTF-16 code units through the text you want to be. [Not sure what a code unit is? See my video &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;w&#x2F;nxfiDc2t4v2dVzaB9UMaz6&quot;&gt;Interesting Characters&lt;&#x2F;a&gt;].&lt;&#x2F;p&gt;
&lt;p&gt;&quot;But isn&#x27;t your cursor always in a text node?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;No - for example, when I have two &amp;lt;br&amp;gt;s next to each other, and I want the cursor to be in between them (so it&#x27;s on the blank line). In this case there is no text node, and browsers handle it weirdly.&lt;&#x2F;p&gt;
&lt;p&gt;In Firefox, when my cursor is between two br nodes, the node is set to the tag (e.g. a div) that contains the brs, and the offset is the index of the second br node in the list.&lt;&#x2F;p&gt;
&lt;p&gt;Other browsers may well do it differently.&lt;&#x2F;p&gt;
&lt;p&gt;So do expect that the nodes may not be text nodes, and where that is the case, the offset will be the index of one of their children.&lt;&#x2F;p&gt;
&lt;p&gt;To select the blank line (between two brs) you can actually specify the br node directly, and give an index number of 0, and it works too. But, it does not match exactly what the browser sets when you click on the blank line, so I can&#x27;t guarantee it works exactly the same.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;backwards-selections&quot;&gt;Backwards selections&lt;&#x2F;h2&gt;
&lt;p&gt;The fly in the ointment is backwards selections: when you click and drag the mouse from right to left, the selection that is created in the browser is backwards: the anchor is on the right and the focus is on the left.&lt;&#x2F;p&gt;
&lt;p&gt;However, if you call setBaseAndExtent attempting to set up a selection like that, the focus and anchor will be swapped so that the anchor is on the left, and the focus is on the right.&lt;&#x2F;p&gt;
&lt;p&gt;But never fear! We can use extend to force the selection to be what we wanted:&lt;&#x2F;p&gt;
&lt;pre&gt;const sel = document.getSelection();
sel.setBaseAndExtent(from_node, from_offset, from_node, from_offset);
sel.extend(to_node, to_offset);
&lt;&#x2F;pre&gt;
&lt;p&gt;Job done.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;go-deeper&quot;&gt;Go deeper&lt;&#x2F;h2&gt;
&lt;p&gt;For more info, check out my demo page: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;browser-selections&#x2F;&quot;&gt;Browser Selections Inventory&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tips for contenteditables</title>
          <pubDate>Tue, 13 Sep 2022 12:58:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/09/13/tips-for-contenteditables/</link>
          <guid>https://artificialworlds.net/blog/2022/09/13/tips-for-contenteditables/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/09/13/tips-for-contenteditables/">&lt;p&gt;I&#x27;ve been working a bit with contenteditable tags in my HTML, and learnt a couple of things, so here they are.&lt;&#x2F;p&gt;
&lt;p&gt;Update: See also my &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;browser-selections&#x2F;&quot;&gt;demo of how to select text in various ways in a contenteditable&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-can-t-i-see-the-cursor-inside-an-empty-contenteditable&quot;&gt;Why can&#x27;t I see the cursor inside an empty contenteditable?&lt;&#x2F;h2&gt;
&lt;p&gt;If you make an editable div like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;div contenteditable=&quot;true&quot;&amp;gt;
&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;and then try to focus it, then sometimes, in some browsers, you won&#x27;t see a cursor.&lt;&#x2F;p&gt;
&lt;p&gt;You can fix it by adding a &amp;lt;br &#x2F;&amp;gt; tag:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;div contenteditable=&quot;true&quot;&amp;gt;
&lt;span style=&quot;color: green;&quot;&gt;&amp;lt;br &#x2F;&amp;gt;&lt;&#x2F;span&gt;
&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Now you should get a cursor and be able to edit text inside.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;programmatically-selecting-text-inside-a-contenteditable&quot;&gt;Programmatically selecting text inside a contenteditable&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s quite tricky to get the browser to select anything. Here&#x27;s a quick recipe for that:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;div id=&quot;ce&quot; contenteditable=&quot;true&quot;&amp;gt;
Some text here
&amp;lt;&#x2F;div&amp;gt;
&amp;lt;script&amp;gt;
const ce = document.getElementById(&quot;ce&quot;);
&lt;span style=&quot;color: green;&quot;&gt;const sel = document.getSelection();
self.setBaseAndExtent(ce.firstChild, 6, ce.lastChild, 10);&lt;&#x2F;span&gt;
&amp;lt;&#x2F;script&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This selects characters 6 to before-10, i.e. the word &quot;text&quot;. To select more complicated stuff inside tags etc. you need to find the actual DOM nodes to pass in to setStart and setEnd, which is quite tricky.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;whenever-you-sethtml-on-a-contenteditable-add-a-br-tag&quot;&gt;Whenever you setHTML on a contenteditable, add a BR tag&lt;&#x2F;h2&gt;
&lt;p&gt;If you use setHTML on a contenteditable you should always append a &amp;lt;br &#x2F;&amp;gt; on the end. It doesn&#x27;t appear in any way, and it prevents weird problems.&lt;&#x2F;p&gt;
&lt;p&gt;Most notably, if you want to have an empty line at the end of your text, you need &lt;strong&gt;two&lt;&#x2F;strong&gt; &amp;lt;br &#x2F;&amp;gt; tags, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;div id=&quot;ce&quot; contenteditable=&quot;true&quot;&amp;gt;
Some text here
&amp;lt;&#x2F;div&amp;gt;
&amp;lt;script&amp;gt;
const ce = document.getElementById(&quot;ce&quot;);
ce.innerHTML = &quot;a&amp;lt;br &#x2F;&amp;gt;&lt;span style=&quot;color: green;&quot;&gt;&amp;lt;br &#x2F;&amp;gt;&lt;&#x2F;span&gt;&quot;
&amp;lt;&#x2F;script&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;If you only include one br tag, there will be no empty line at the end.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;selecting-the-end-of-a-contenteditable&quot;&gt;Selecting the end of a contenteditable&lt;&#x2F;h2&gt;
&lt;p&gt;It&#x27;s surprisingly tricky to put the cursor at the end of a contenteditable div but here is a recipe that works:&lt;&#x2F;p&gt;
&lt;pre&gt;const range = document.createRange();
&lt;span style=&quot;color: green;&quot;&gt;range.selectNodeContents(ce);
range.collapse();&lt;&#x2F;span&gt;
const sel = document.getSelection();
sel.removeAllRanges();
sel.addRange(range);
&lt;&#x2F;pre&gt;
&lt;p&gt;(Where ce is our contenteditable div.)&lt;&#x2F;p&gt;
&lt;p&gt;NOTE: this won&#x27;t work if you don&#x27;t add a br tag at the end, as described above!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;update-beware-of-newlines-in-your-source-html&quot;&gt;Update: beware of newlines in your source HTML&lt;&#x2F;h2&gt;
&lt;p&gt;Note: not &amp;lt;br&amp;gt; tags, but actual newlines in your HTML source, can cause weird behaviour, especially around selections.&lt;&#x2F;p&gt;
&lt;p&gt;For example, if your div ends with a newline, you can&#x27;t select the end of it by doing this:&lt;&#x2F;p&gt;
&lt;pre&gt;const sel = document.getSelection();
sel.selectAllChildren(editor);
sel.collapseToEnd();&lt;&#x2F;pre&gt;
&lt;p&gt;The above will work (put your cursor at the end of the text) if there is no newline in the source, but not work (make your cursor disappear or jump to the right of the div) if there is a newline.&lt;&#x2F;p&gt;
&lt;p&gt;So if you&#x27;re programmatically generating the HTML, I recommend removing this &quot;extraneous&quot; whitespace if you can.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more-tips&quot;&gt;More tips?&lt;&#x2F;h2&gt;
&lt;p&gt;Any more tips? Drop them in the comments and I&#x27;ll include them.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Outreachy August 2022 update</title>
          <pubDate>Fri, 26 Aug 2022 08:29:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/08/26/outreachy-august-2022-update/</link>
          <guid>https://artificialworlds.net/blog/2022/08/26/outreachy-august-2022-update/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/08/26/outreachy-august-2022-update/">&lt;p&gt;I had the pleasure of being a mentor this summer for an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.outreachy.org&#x2F;&quot;&gt;Outreachy&lt;&#x2F;a&gt; internship for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; organisation. Outreachy provides internships to people subject to systemic bias and impacted by underrepresentation in the technical industry where they are living.&lt;&#x2F;p&gt;
&lt;p&gt;Outreachy is a fantastic organisation doing a brilliant job to try and make our sometimes terrible industry a little bit better.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.outreachy.org&#x2F;&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;outreachy.png&quot; alt=&quot;Outreachy logo&quot; width=&quot;210&quot; height=&quot;47&quot; class=&quot;aligncenter size-full wp-image-3463&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Mentoring was great fun, mainly because it was such a pleasure working with my awesome intern &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;yaya-usman.hashnode.dev&#x2F;&quot;&gt;Usman&lt;&#x2F;a&gt;. There is lots of support available for interns and mentors through Outreachy&#x27;s Zulip chat (when will we persuade them to use Matrix? ;-) so you always have somewhere to turn if you have questions.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to read more about the internship from Usman&#x27;s point of view, check out his blog posts:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;yaya-usman.hashnode.dev&#x2F;outreachy-blog-introducing-myself&quot;&gt;Outreachy Blog - Introducing Myself&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;yaya-usman.hashnode.dev&#x2F;wrap-up-summary-of-my-journey-to-being-an-outreachy-intern-at-element&quot;&gt;Wrap-up: Summary of my journey to being an outreachy intern at Element&lt;&#x2F;a&gt; We talked every day on video calls, and really enjoyed working together. Some days we would just chat, sometimes I would give pointers for things to try in the code, or people to talk to. Some days we worked through some code together, and that was the most fun. Usman is incredibly enthusiastic and bright, so it was very satisfying making suggestions and seeing him put them into practice.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;success&quot;&gt;Success!&lt;&#x2F;h3&gt;
&lt;p&gt;The work went very well, and Usman succeeded in creating a prototype that will help us design the Favourite Messages feature:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=nrrNcl6_FOo&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;nrrNcl6_FOo&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note: the feature isn&#x27;t ready to be fully released because it needs to be implemented on mobile platforms as well as changing where it stores its information: currently we use the browser&#x27;s local storage, but we plan to store things in Matrix, meaning it is automatically synchronised between devices.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;things-that-went-well&quot;&gt;Things that went well&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Meeting every day&lt;&#x2F;strong&gt;: we talked on a short video call every morning. This meant if we misunderstood each other it was quickly resolved, without lots of time being wasted.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Having a clear list of tasks&lt;&#x2F;strong&gt;: we kept a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;vector-im&#x2F;element-web&#x2F;issues&#x2F;22453&quot;&gt;tracking issue&lt;&#x2F;a&gt; on Github. This meant were clear what Usman was supposed to be doing now, and what was coming next.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Being flexible&lt;&#x2F;strong&gt;: we worked together to change the list of tasks every week or so. This meant we were being realistic about what could be achieved, and able to change in response to things we found out, or feedback from others.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Getting design input&lt;&#x2F;strong&gt;: we talked to Element&#x27;s designers several times during the project, showing them prototypes and early implementations. This meant we didn&#x27;t waste time implementing things that would need redesign later.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Support for me&lt;&#x2F;strong&gt;: I was able to work with Thib, who is our Outreachy Matrix community organiser, especially during the selection process. This meant I was not making decisions in isolation, and had support if anything tricky came up. My manager, Johannes was also a great support for me trying to learn a little about how to be a manager.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The Element Web community&lt;&#x2F;strong&gt;: Usman got loads of support from our community. Special thanks to Šimon, Olivier, Shay and t3chguy for your help!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Element the company&lt;&#x2F;strong&gt;: Element paid for this internship, and gave great support to Usman, integrating him into all our systems, inviting him to introductory meetings etc. He had every opportunity to see what working at Element is like, and to make an impression on everyone here. Element did a great job here.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;things-i-would-do-differently&quot;&gt;Things I would do differently&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Managing the contribution period&lt;&#x2F;strong&gt;: before the project began, applicants are invited to contribute to the projects, allowing us to choose an intern based on those contributions. I felt slightly disorganised at this stage, and there was a lot of activity in issues and pull requests in the project from applicants. I think I should have warned our community and explained what was going to happen up-front, and maybe enlisted help from people willing to triage the contributions a little. Contributions varied in quality and understanding level, so having some volunteers who were primed to spend a little more time explaining and helping contributors get started would have prevented this impinging on the time of the team as a whole. Nevertheless, our team responded really well, and we got some useful contributions, and I hope the contributors had a good experience too.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Integrating Usman into the team&lt;&#x2F;strong&gt;: we chose a project that was independent from what other team members were doing, meaning he mostly interacted with others when he needed help. While it is sensible to make sure interns are decoupled from the main work (because it&#x27;s hard to predict how much progress they will make, and they are going stop after their internship), I do also wish we could have found a project that gave more opportunity to work with other people, not just &quot;stealing&quot; their time to help out, but actually working together on shared pieces of work. This is a tricky one to figure out, but food for thought.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h3&gt;
&lt;p&gt;The experience of being a mentor was really fun, and I would recommend it to anyone working on an open source project. I would emphasise, though, that you need to &lt;strong&gt;put aside enough time&lt;&#x2F;strong&gt;: the internship will not be successful if you don&#x27;t make time to work with your intern, get to know them, and introduce them to your community. Since interns may be new to the world of work, or shy about taking your time, as a mentor, you need to take responsibility for giving them enough support. Final note: as a mentor, you are NOT responsible for the work going well! Your responsibility is to help and support your intern, and give them everything they need to be successful (including feedback about things that are not working well), but it is up to the the intern themself to do the work, and how much work gets done is going to be the combination of a number of factors, including the intern&#x27;s experience and abilities. Don&#x27;t worry if you don&#x27;t get as far as you expected - after all, that happens in nearly all software projects...&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Transcoding video files for playback in a browser</title>
          <pubDate>Sat, 06 Aug 2022 23:21:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/08/06/transcoding-video-files-for-playback-in-a-browser/</link>
          <guid>https://artificialworlds.net/blog/2022/08/06/transcoding-video-files-for-playback-in-a-browser/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/08/06/transcoding-video-files-for-playback-in-a-browser/">&lt;pre&gt;ffmpeg -i original.mkv -c:v libx264 -c:a aac -ac 2 -ab 384000 -ar 48000 new.mp4&lt;&#x2F;pre&gt;
&lt;p&gt;(Short answer: use the above ffmpeg command line. Read on for how I did this in Tdarr.)&lt;&#x2F;p&gt;
&lt;p&gt;I recently discovered &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jellyfin.org&#x2F;&quot;&gt;Jellyfin&lt;&#x2F;a&gt;, which gives me a Netflix-like UI for viewing my own videos, and seems great.&lt;&#x2F;p&gt;
&lt;p&gt;The only problem I had was that some videos were in formats that can&#x27;t be played natively in a web browser. Jellyfin heroically tries to transcode them on the fly, but my server is very lightweight, and there&#x27;s no way it can do that.&lt;&#x2F;p&gt;
&lt;p&gt;So, I needed to transcode those videos to a more suitable format.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;HaveAGitGat&#x2F;Tdarr&quot;&gt;Tdarr&lt;&#x2F;a&gt; allows transcoding large numbers of files, and with a little head-scratching I worked out how to get it running, but I still needed the right ffmpeg options to make the videos work well in Firefox, without needing transcoding of video, audio or the container.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the Tdarr settings that I found worked well:&lt;&#x2F;p&gt;
&lt;pre&gt;Output file container: .mp4
Encoder: ffmpeg
CLI arguments: &lt;io&gt;-c:v libx264 -c:a aac -ac 2 -ab 384000 -ar 48000
Only transcode videos in these codecs: hevc&lt;&#x2F;io&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Explanation:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Output file container: .mp4 - wraps the video up in an MP4 container - surprisingly, Firefox doesn&#x27;t seem to support MKV.&lt;&#x2F;li&gt;
&lt;li&gt;-c:v libx264 - re-encodes the video as H.264. Firefox can&#x27;t do H.265, and H.264 is reasonably compatible with lots of browsers. If you don&#x27;t care about Safari or various Microsoft browsers, you might want to think about VP9 as it&#x27;s natively supported on Firefox, so should work on weird architectures etc.&lt;&#x2F;li&gt;
&lt;li&gt;-c:a aac -ac 2 -ab 384000 -ar 48000 - re-encodes the audio as AAC with the right bitrates etc. Jellyfin was still transcoding the audio when I just specified -c:a aac, and it took me a while to work out that you need those other options too.&lt;&#x2F;li&gt;
&lt;li&gt;Only transcode videos in these codecs: hevc - hevc means H.265 encoding, and the videos I had problems with were in that encoding, but you might have different problems. If in doubt, you can choose &quot;Don&#x27;t transcode videos in these codecs:&quot; and uncheck all the encodings, meaning all your videos will be re-encoded.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you are not using Tdarr, here is the plain command line to use with ffmpeg:&lt;&#x2F;p&gt;
&lt;pre&gt;ffmpeg -i original.mkv -c:v libx264 -c:a aac -ac 2 -ab 384000 -ar 48000 new.mp4&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Matrix is a Distributed Real-time Database Video</title>
          <pubDate>Thu, 21 Jul 2022 18:23:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/07/21/matrix-is-a-distributed-real-time-database-video/</link>
          <guid>https://artificialworlds.net/blog/2022/07/21/matrix-is-a-distributed-real-time-database-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/07/21/matrix-is-a-distributed-real-time-database-video/">&lt;p&gt;Curious to know a bit more about Matrix? This video goes into the details of what kinds of requests you need to send to write a Matrix client, and why it&#x27;s interesting to write a Matrix server.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Matrix is a Distributed Real-time Database&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d6cfd099-80d1-42e2-8eb9-cf281cabf70b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;matrix-is-a-distributed-real-time-database&#x2F;matrix-is-a-distributed-real-time-database.html#title2&quot;&gt;Matrix is a Distributed Real-time Database Slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Really excited that since I started my job working on Matrix, I have become more enthusiastic about it, rather than less.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building cross-platform Rust for Web, Android and iOS - a minimal example</title>
          <pubDate>Wed, 06 Jul 2022 15:11:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/07/06/building-cross-platform-rust-for-web-android-and-ios-a-minimal-example/</link>
          <guid>https://artificialworlds.net/blog/2022/07/06/building-cross-platform-rust-for-web-android-and-ios-a-minimal-example/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/07/06/building-cross-platform-rust-for-web-android-and-ios-a-minimal-example/">&lt;p&gt;One of the advantages of writing code in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; is that it can be re-used in other places. Both iOS and Android allow using native libraries within your apps, and Rust compiles to native. Web pages can now use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;webassembly.org&#x2F;&quot;&gt;WebAssembly&lt;&#x2F;a&gt; (WASM), and Rust can compile to WASM.&lt;&#x2F;p&gt;
&lt;p&gt;So, it should be easy, right?&lt;&#x2F;p&gt;
&lt;p&gt;Well, in practice it seems a little tricky, so I created a small example project to explain it to myself, so maybe it&#x27;s helpful to you too.&lt;&#x2F;p&gt;
&lt;p&gt;The full code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&lt;&#x2F;a&gt;, but here is the general idea:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;crates&#x2F;example-rust-bindings&lt;&#x2F;strong&gt; - the real Rust code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;bindings&#x2F;ffi&lt;&#x2F;strong&gt; - uniffi code to build shared objects for Android and iOS&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;bindings&#x2F;wasm&lt;&#x2F;strong&gt; - wasm_bingen code to build WASM for Web&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;examples&#x2F;example-android&lt;&#x2F;strong&gt; - an Android app that generates a Kotlin wrapper, and runs the code in the shared object&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;examples&#x2F;example-ios&lt;&#x2F;strong&gt; - an iOS XCode project where we generate Swift bindings, so we can call the code in the shared object&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;examples&#x2F;example-web&lt;&#x2F;strong&gt; - a web page that imports the WASM and runs it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;steps-for-wasm&quot;&gt;Steps for WASM&lt;&#x2F;h2&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wasm-firefox.png&quot; alt=&quot;Proof that I did this on Web - Firefox showing &amp;quot;This string is from Rust!&amp;quot;&quot; width=&quot;456&quot; height=&quot;340&quot; class=&quot;aligncenter size-full wp-image-3434&quot;&gt;
&lt;ul&gt;
&lt;li&gt;Write normal Rust code e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&quot;&gt;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt; (and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;Cargo.toml&quot;&gt;Cargo.toml&lt;&#x2F;a&gt; etc. to go with it, with nothing special in it)&lt;&#x2F;li&gt;
&lt;li&gt;Write wasm-bindgen wrappers for all interfaces you want to expose e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;wasm&#x2F;src&#x2F;lib.rs&quot;&gt;bindings&#x2F;wasm&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt; (and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;wasm&#x2F;Cargo.toml&quot;&gt;Cargo.toml&lt;&#x2F;a&gt; to go with it, depending on wasm-bindgen). This basically involves creating structs and functions annotated with #[wasm_bindgen] that call through to the real underlying code from the previous step.&lt;&#x2F;li&gt;
&lt;li&gt;Write a package.json with a build step that calls wasm-pack e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;wasm&#x2F;package.json#L31&quot;&gt;bindings&#x2F;wasm&#x2F;package.json&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Build the bindings with npm (see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;wasm&#x2F;README.md&quot;&gt;bindings&#x2F;wasm&#x2F;README.md&lt;&#x2F;a&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Copy the generated .js and .wasm files into a web project&lt;&#x2F;li&gt;
&lt;li&gt;Include the generated .js file into an HTML file using an import statement in a module e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;examples&#x2F;example-web&#x2F;index.html&quot;&gt;examples&#x2F;example-web&#x2F;index.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Variation: if you modify the build script in package.json to call wasm-pack with --target node instead of --target web you can generate code suitable for using from a NodeJS module.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;steps-for-android&quot;&gt;Steps for Android&lt;&#x2F;h2&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;native-android.png&quot; alt=&quot;Proof that I did this on Android: Android emulator showing a label &amp;quot;This string is from Rust!&amp;quot;&quot; width=&quot;241&quot; height=&quot;507&quot; class=&quot;aligncenter size-full wp-image-3435&quot;&gt;
&lt;ul&gt;
&lt;li&gt;Write normal Rust code e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&quot;&gt;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt; (and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;Cargo.toml&quot;&gt;Cargo.toml&lt;&#x2F;a&gt; etc. to go with it, with nothing special in it)&lt;&#x2F;li&gt;
&lt;li&gt;Write a UDL file to describe your interfaces e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;src&#x2F;my_rust_code.udl&quot;&gt;bindings&#x2F;ffi&#x2F;src&#x2F;my_rust_code.udl&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Implement those interfaces in some Rust code, wrapping returned values in Arc e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;src&#x2F;lib.rs&quot;&gt;bindings&#x2F;ffi&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Write a Cargo.toml that builds the bindings e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;Cargo.toml&quot;&gt;bindings&#x2F;ffi&#x2F;Cargo.toml&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Generate uniffi bindings scaffolding during the Cargo build by writing a build.rs file e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;build.rs&quot;&gt;bindings&#x2F;ffi&#x2F;build.rs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Get the Android NDK and set up Cargo&#x27;s config to use it to build - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Cross-compile for the Android platforms you need by adding the targets using rustup and then building using cargo build --target=blah - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Copy the built .so shared object files into your Android project under &lt;code&gt;jniLibs&#x2F;INSERT\_PLATFORM&lt;&#x2F;code&gt; - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;In your Android project, add a android.applicationVariants.all section to your app&#x2F;build.gradle that generates Kotlin wrappers around the shared objects e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;examples&#x2F;example-android&#x2F;app&#x2F;build.gradle&quot;&gt;examples&#x2F;example-android&#x2F;app&#x2F;build.gradle&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Now you can write normal Kotlin that accesses your Rust code via a namespace like uniffi.my_rust_code e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;examples&#x2F;example-android&#x2F;app&#x2F;src&#x2F;main&#x2F;java&#x2F;net&#x2F;artificialworlds&#x2F;exampleandroid&#x2F;MainActivity.kt&quot;&gt;MainActivity.kt&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;steps-for-ios&quot;&gt;Steps for iOS&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Write normal Rust code e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&quot;&gt;crates&#x2F;example-rust-bindings&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt; (and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;crates&#x2F;example-rust-bindings&#x2F;Cargo.toml&quot;&gt;Cargo.toml&lt;&#x2F;a&gt; etc. to go with it, with nothing special in it)&lt;&#x2F;li&gt;
&lt;li&gt;Write a UDL file to describe your interfaces e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;src&#x2F;my_rust_code.udl&quot;&gt;bindings&#x2F;ffi&#x2F;src&#x2F;my_rust_code.udl&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Implement those interfaces in some Rust code, wrapping returned values in Arc e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;src&#x2F;lib.rs&quot;&gt;bindings&#x2F;ffi&#x2F;src&#x2F;lib.rs&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Write a Cargo.toml that builds the bindings e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;Cargo.toml&quot;&gt;bindings&#x2F;ffi&#x2F;Cargo.toml&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Generate uniffi bindings - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md#ios-1&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Cross-compile for the iOS platforms you need by adding the targets using rustup and then building using cargo build --target=blah - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Combine together the built libraries using lipo - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Copy the combined .a library file into your XCode project - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Generate an XCode project with xcodebuild - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;example-rust-bindings&#x2F;-&#x2F;blob&#x2F;main&#x2F;bindings&#x2F;ffi&#x2F;README.md&quot;&gt;bindings&#x2F;ffi&#x2F;README.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Now you can write normal Swift that accesses your Rust code [TODO: details]&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Deporting desperate people from the UK</title>
          <pubDate>Mon, 06 Jun 2022 12:04:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/06/06/deporting-desperate-people-from-the-uk/</link>
          <guid>https://artificialworlds.net/blog/2022/06/06/deporting-desperate-people-from-the-uk/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/06/06/deporting-desperate-people-from-the-uk/">&lt;p&gt;Letter to my MP on deporting refugees to Rwanda, 2022-06-06.&lt;&#x2F;p&gt;
&lt;p&gt;Dear Ben Spencer,&lt;&#x2F;p&gt;
&lt;p&gt;Please do what you can to reverse the policy of sending asylum seekers to Rwanda.&lt;&#x2F;p&gt;
&lt;p&gt;We are breaking our proud tradition of commitment to refugees.&lt;&#x2F;p&gt;
&lt;p&gt;This policy seems to have the intention of preventing people from drowning while attempting to enter the UK. Instead of ruling people&#x27;s claims &quot;inadmissable&quot; because they were desperate enough to enter by a dangerous route, we should provide safe routes for people escaping war and harm.&lt;&#x2F;p&gt;
&lt;p&gt;I am sure this policy was drafted with good intent, but immediately it started we have seen disproportionate numbers of Sudanese people being deported to Rwanda [1] verses other nationalities. Even within the parameters of its own flawed morality, this policy is unfair in practice. It should be stopped immediately.&lt;&#x2F;p&gt;
&lt;p&gt;[1] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theguardian.com&#x2F;uk-news&#x2F;2022&#x2F;jun&#x2F;06&#x2F;home-office-offers-asylum-seekers-choice-between-war-zones-they-fled-and-rwanda&quot;&gt;https:&#x2F;&#x2F;www.theguardian.com&#x2F;uk-news&#x2F;2022&#x2F;jun&#x2F;06&#x2F;home-office-offers-asylum-seekers-choice-between-war-zones-they-fled-and-rwanda&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
&lt;p&gt;If you want to write a similar letter, feel free to use any of the above if it&#x27;s helpful. I used &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&#x2F;&quot;&gt;WriteToThem.com&lt;&#x2F;a&gt; as a very easy way to find your MP and send a message.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Improving my vimrc live on stream</title>
          <pubDate>Thu, 28 Apr 2022 07:22:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/04/28/improving-my-vimrc-live-on-stream/</link>
          <guid>https://artificialworlds.net/blog/2022/04/28/improving-my-vimrc-live-on-stream/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/04/28/improving-my-vimrc-live-on-stream/">&lt;p&gt;I was becoming increasingly uncomfortable with how crufty my neovim config was getting, and especially how I didn&#x27;t understand parts of it, so I decided to wipe it clean and rebuild it from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;I did it live on stream, to make it feel like a worthwhile activity:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Crafting .vimrc from scratch&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d1c14d00-c3aa-4a53-a9d4-97c2730ea6bd&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Headline features of the new vimrc:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A new theme, using the base16 theme framework&lt;&#x2F;li&gt;
&lt;li&gt;A file browser (NERDTree)&lt;&#x2F;li&gt;
&lt;li&gt;A minimalist status line with vim-airline&lt;&#x2F;li&gt;
&lt;li&gt;Search with ripgrep&lt;&#x2F;li&gt;
&lt;li&gt;Rust language support with Coc&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note: after the stream I managed to resolve the remaining issues with highlight colours not showing by triggering re-applying them after the theme has been applied:&lt;&#x2F;p&gt;
&lt;pre&gt;augroup tabs_in_make
    autocmd!
    autocmd ColorScheme * highlight MatchParen cterm=none ctermbg=none ctermfg=green
augroup END&lt;&#x2F;pre&gt;
&lt;p&gt;You can find my current neovim config at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;configs&#x2F;-&#x2F;tree&#x2F;main&#x2F;.config&#x2F;nvim&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;configs&#x2F;-&#x2F;tree&#x2F;main&#x2F;.config&#x2F;nvim&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Comparison of Matrix events before and after &quot;Extensible Events&quot;</title>
          <pubDate>Tue, 08 Mar 2022 11:34:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/03/08/comparison-of-matrix-events-before-and-after-extensible-events/</link>
          <guid>https://artificialworlds.net/blog/2022/03/08/comparison-of-matrix-events-before-and-after-extensible-events/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/03/08/comparison-of-matrix-events-before-and-after-extensible-events/">&lt;p&gt;[Updated 2022-11-17 based on the new draft of the MSC, notably removing backwards compatibility and the abbreviated forms.]&lt;&#x2F;p&gt;
&lt;p&gt;(Background: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; is the awesome open standard for messaging that I get to work on now that I work at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;element.io&#x2F;&quot;&gt;Element&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;blob&#x2F;matthew&#x2F;msc1767&#x2F;proposals&#x2F;1767-extensible-events.md&quot;&gt;Extensible Events (MSC1767)&lt;&#x2F;a&gt; Matrix Spec Change proposal describes a new way of structuring events in matrix that makes it easy to send events that have multiple representations (e.g. something clever like an interactive map, and something simpler like an image of a map).&lt;&#x2F;p&gt;
&lt;p&gt;The change has 2 main purposes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;To make it easy for clients that don&#x27;t support some amazing new feature to display something that is still useful.&lt;&#x2F;li&gt;
&lt;li&gt;To allow re-using the definitions of parts of events in other events.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Since there is an implementation of this change out in the wild (in Element), it seems reasonably likely that this change will be accepted into the Matrix spec.&lt;&#x2F;p&gt;
&lt;p&gt;I really like this change, but I find it hard to understand, so here is a simple example that I have found helpful to think it through.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-old-event-and-a-new-event&quot;&gt;An old event, and a new event&lt;&#x2F;h2&gt;
&lt;p&gt;Here is an old-fashioned event, followed by a new, shiny, extensible version:&lt;&#x2F;p&gt;
&lt;pre&gt;{
    &quot;type&quot;: &quot;m.room.message&quot;,
    &quot;content&quot;: {
        &lt;span style=&quot;color: red;&quot;&gt;&quot;body&quot;: &quot;This is the *old* way&quot;,
        &quot;format&quot;: &quot;org.matrix.custom.html&quot;,
        &quot;formatted_body&quot;: &quot;This is the &amp;lt;b&amp;gt;old&amp;lt;&#x2F;b&amp;gt; way&quot;,
        &quot;msgtype&quot;: &quot;m.text&quot;&lt;&#x2F;span&gt;
    },
    &lt;i&gt;... other properties not relevant to this, e.g. &quot;sender&quot; ...&lt;&#x2F;i&gt;
}
&lt;&#x2F;pre&gt;
&lt;pre&gt;{
    &quot;type&quot;: &quot;m.message&quot;,
    &quot;content&quot;: {
        &lt;span style=&quot;color: green;&quot;&gt;&quot;m.markup&quot;: [
            {&quot;mimetype&quot;: &quot;text&#x2F;plain&quot;, &quot;body&quot;: &quot;This the *new* way&quot;},
            {&quot;mimetype&quot;: &quot;text&#x2F;html&quot;, &quot;body&quot;: &quot;This is the &amp;lt;b&amp;gt;new&amp;lt;&#x2F;b&amp;gt; way&quot;}
        ],&lt;&#x2F;span&gt;
    }
    &lt;i&gt;... other properties not relevant to this, e.g. &quot;sender&quot; ...&lt;&#x2F;i&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;The point is that as well as normal contents of the message (here, the m.markup &quot;content block&quot;) we can have other representations of the same message, such as an image, location co-ordinates, or something completely different. The client will render using the content blocks it expects if it knows about this event type (and is able to show it), but if not, it can look for other content blocks that it does understand.&lt;&#x2F;p&gt;
&lt;p&gt;For example, in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-spec-proposals&#x2F;blob&#x2F;travis&#x2F;msc&#x2F;polls&#x2F;proposals&#x2F;3381-polls.md&quot;&gt;Polls&lt;&#x2F;a&gt; when you send a new poll question, it could look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;{
    &quot;type&quot;: &quot;m.poll.start&quot;,
    &quot;content&quot;: {
        &lt;span style=&quot;color: green;&quot;&gt;&quot;m.poll&quot;: {
            &lt;i&gt;... The actual poll question etc. ...&lt;&#x2F;i&gt;
        },
        &quot;m.markup&quot;: [
            &lt;i&gt;... A text version of the question ...&lt;&#x2F;i&gt;
        ]&lt;&#x2F;span&gt;
    },
    &lt;i&gt;... other properties not relevant to this, e.g. &quot;sender&quot; ...&lt;&#x2F;i&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;So clients that don&#x27;t know m.poll.start can still display the poll question (if they understand extensible events), instead of completely ignoring event types they don&#x27;t know about.&lt;&#x2F;p&gt;
&lt;p&gt;Notice that sometimes content blocks (inside content) can have the same name as the event type, but they don&#x27;t have to.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Air-Source Heat Pump - our experience so far, 2 months in</title>
          <pubDate>Sun, 09 Jan 2022 01:35:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2022/01/09/air-source-heat-pump-our-experience-so-far-2-months-in/</link>
          <guid>https://artificialworlds.net/blog/2022/01/09/air-source-heat-pump-our-experience-so-far-2-months-in/</guid>
          <description xml:base="https://artificialworlds.net/blog/2022/01/09/air-source-heat-pump-our-experience-so-far-2-months-in/">&lt;p&gt;[Update: see also &lt;a href=&quot;&#x2F;blog&#x2F;2022&#x2F;11&#x2F;24&#x2F;air-source-heat-pump-1-year-later&#x2F;&quot;&gt;Air-Source Heat Pump – 1 year later&lt;&#x2F;a&gt;.]&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary-less-energy-more-money&quot;&gt;Summary: less energy, more money&lt;&#x2F;h2&gt;
&lt;p&gt;2 months ago, we replaced our gas boiler with an air-source heat pump, which uses electricity to heat our home and boiler. This is a report of our experience so far.&lt;&#x2F;p&gt;
&lt;p&gt;We expected it to reduce our environmental impact, and cost us more money, and we were right.&lt;&#x2F;p&gt;
&lt;p&gt;It works: our house is comfortable. We use a lot &lt;strong&gt;less energy&lt;&#x2F;strong&gt;, and it costs us significantly &lt;strong&gt;more money&lt;&#x2F;strong&gt; (because electricity costs way more than gas).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-house&quot;&gt;The house&lt;&#x2F;h2&gt;
&lt;p&gt;Our house is a beautiful, leaky old house, with a modern extension. Half of it is well-insulated. The other half was built around 1890, and while we do have double-glazing and decent loft insulation, the walls have no cavities and feel cold to the touch, and there are drafts everywhere.&lt;&#x2F;p&gt;
&lt;p&gt;The new half has underfloor heating. The old half and the upstairs are heated by radiators. We have a hot water cylinder.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-air-source-heat-pump&quot;&gt;The air-source heat pump&lt;&#x2F;h2&gt;
&lt;p&gt;Our air-source heat pump &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.yourenergyyourway.co.uk&#x2F;2018&#x2F;09&#x2F;30&#x2F;energy-from-thin-air&#x2F;&quot;&gt;uses electricity to extract heat from the outside air&lt;&#x2F;a&gt; and heats water for radiators and hot water, directly replacing our gas boiler.&lt;&#x2F;p&gt;
&lt;p&gt;Our heat pump was installed by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.yourenergyyourway.co.uk&#x2F;&quot;&gt;Your Energy Your Way&lt;&#x2F;a&gt; and I must declare in interest: my wife is a director of the company.&lt;&#x2F;p&gt;
&lt;p&gt;The heat pump is an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lg.com&#x2F;uk&#x2F;heating-awhp&quot;&gt;LG 16kW &quot;THERMA V&quot; model&lt;&#x2F;a&gt;. It looks like a very large air conditioning unit, which sits outside our house in the yard to the side. It is about as tall as my shoulder height, with two big fans on it.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;heat-pump.jpg&quot; alt=&quot;A large air-source heat pump&quot; width=&quot;853&quot; height=&quot;910&quot; class=&quot;aligncenter size-full wp-image-3377&quot;&gt;
&lt;p&gt;It stands on a soak-away area with some stones on it that the installers made by removing some patio tiles. This is needed because it drips a small amount of liquid as part of its normal operation. The outdoor unit makes noise, but our house is next to the main road, so we don&#x27;t hear it. It is not audible indoors.&lt;&#x2F;p&gt;
&lt;p&gt;Standing next to the outdoor unit you can feel a cold breeze, like opening the fridge door. This is unpleasant on cold days.&lt;&#x2F;p&gt;
&lt;p&gt;That outdoor unit connects through the wall to an indoor part that is a bit smaller than our old boiler.&lt;&#x2F;p&gt;
&lt;p&gt;The controller box has a terrible user interface and is very hard to decipher, but we did eventually manage to programme it to turn the target temperature up in the daytime and down at night. Your Energy Your Way advised us that it is more efficient to keep the house at a cool-ish 17 degrees at night, rather than letting it get cold and having to work hard heat it up again in the morning, so that is how we have set it up.&lt;&#x2F;p&gt;
&lt;p&gt;The controller box&#x27;s built-in thermostat does not work properly (it reports the wrong temperature), so we had to add an external thermostat, which works well.&lt;&#x2F;p&gt;
&lt;p&gt;We didn&#x27;t need to change anything about our hot water cylinder, or our underfloor heating.&lt;&#x2F;p&gt;
&lt;p&gt;When planning the installation, Your Energy Your Way estimated the heat loss of our rooms, and recommended upgrading our radiators. In an old house like ours this is sometimes needed, because it is way more efficient to heat a house with cooler water running through the radiators, but if the water is cooler, you need more radiator surface area to heat the house effectively. In a newer house with existing radiators, they are probably fine as-is.&lt;&#x2F;p&gt;
&lt;p&gt;We kept most of the existing radiators, and added some more in the coldest rooms.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-comfortable-is-the-house&quot;&gt;How comfortable is the house?&lt;&#x2F;h2&gt;
&lt;p&gt;The house is more comfortable than it was before, for two reasons: firstly the radiators we had were not really adequate, and secondly the cooler water in the radiators makes a less irritating heat, meaning the house is nicely comfortable most of the time, instead of bouncing between feeling cold and feeling oppressively over-heated.&lt;&#x2F;p&gt;
&lt;p&gt;On cold days, the old part of the house is a bit cold, but I think on average it&#x27;s a little better than it was before.&lt;&#x2F;p&gt;
&lt;p&gt;We do find mornings can be chilly, particularly because the system stops heating the radiators if the hot water cylinder needs heating up after people have had showers. We could improve this situation by getting a larger cylinder, which we are considering.&lt;&#x2F;p&gt;
&lt;p&gt;However, it&#x27;s worth pointing out that we needed engineers to visit four or five times to make adjustments before we felt the system was working well enough. There are a lot of things that can be tweaked, and it took some time for it to work well.&lt;&#x2F;p&gt;
&lt;p&gt;My advice: don&#x27;t pick the cheapest quote - pick the people you think you can trust to do the work well: especially the heat loss calculations before installation and the adjustments afterwards.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-much-energy-are-we-using-the-good-news&quot;&gt;How much energy are we using? (The good news)&lt;&#x2F;h2&gt;
&lt;p&gt;So far, it looks like we are using about &lt;strong&gt;two-thirds less&lt;&#x2F;strong&gt; energy in our household than we were before:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;energy-usage.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The above chart is stacked, so the top line represents the total energy usage. We switched to the air-source heat pump exactly when our gas usage was about to skyrocket (because it&#x27;s cold in winter), and it remained relatively low.&lt;&#x2F;p&gt;
&lt;p&gt;This is absolutely fantastic: our house is more comfortable than before, and we have reduced the amount of energy we are using by 66%. This is the total energy usage of our house, not just for heating, so the reduction of energy used for heating is even more dramatic than it looks.&lt;&#x2F;p&gt;
&lt;p&gt;Even better, the energy we use is at least partly produced from renewable sources, so our carbon footprint is much lower. Previously we were directly releasing carbon by burning imported gas - now we use mostly UK-produced electricity, and as the grid decarbonises, our carbon footprint reduces even if we make no further changes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-much-money-are-we-spending-the-bad-news&quot;&gt;How much money are we spending? (The bad news)&lt;&#x2F;h2&gt;
&lt;p&gt;Excluding standing charges*, we are spending about &lt;strong&gt;one third more&lt;&#x2F;strong&gt; on energy than we were before. This is because electricity is so much more expensive than gas: our electricity costs 19p per kWh and our gas costs 4p per kWh.&lt;&#x2F;p&gt;
&lt;p&gt;* Note: our energy provider wanted to charge us £350 to remove our gas meter, so we refused, and are still paying the gas standing charge. I&#x27;m not sure how we&#x27;re going to resolve this, especially since our energy provider is now in administration.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;energy-cost.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The above chart is stacked, so the top line represents the total cost (excluding standing charges). When we switched to the air-source heat pump, our energy costs increased faster than they did the same time last year, and were consistently higher. We think the peak in November might be misleading as it may have been when the system was not set up correctly, but we are not sure.&lt;&#x2F;p&gt;
&lt;p&gt;Because air-source heat pumps are more efficient when the weather is warmer, we do expect to fare better in the summer than we are right now.&lt;&#x2F;p&gt;
&lt;p&gt;I would not suggest getting a heat pump if you want to save money. Maybe this will change as gas prices are expected to rise significantly this year.&lt;&#x2F;p&gt;
&lt;p&gt;An installation like ours, including new radiators, costs £10-15K. A decent chunk of that will be paid back to us by the government, spread out over the next 7 years, under the soon-to-be-gone &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gov.uk&#x2F;domestic-renewable-heat-incentive&quot;&gt;Renewable Heat Incentive&lt;&#x2F;a&gt; (RHI). RHI will be replaced by the&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ofgem.gov.uk&#x2F;environmental-and-social-schemes&#x2F;boiler-upgrade-scheme-bus&quot;&gt;Boiler Upgrade Scheme&lt;&#x2F;a&gt; (BUS), which will be limited to a £5K grant for air-source heat pumps, although it is paid up-front. We would have received much less money under BUS than RHI. It is almost certainly too late for you to get a heat pump under RHI, by the way - all the installers are booked up until end of March 2022, when it ends.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thoughts&quot;&gt;Thoughts&lt;&#x2F;h2&gt;
&lt;p&gt;If you think it&#x27;s surprising (and deeply concerning) that taking the step of significantly reducing our carbon footprint cost us a one-third increase in our energy bills, I would agree with you.&lt;&#x2F;p&gt;
&lt;p&gt;I am told that the tax taken on electricity is much higher than on gas, even though these taxes are apparently intended help decarbonise our energy.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, the government is replacing (with great fanfare) RHI with the much less generous (although more timely) BUS, making it even more economically punishing to reduce your carbon footprint.&lt;&#x2F;p&gt;
&lt;p&gt;I think this should be addressed urgently: &lt;strong&gt;money should be provided to help people install heat pumps, and the tax regime should be changed to make it cheap to use low-carbon fuels.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The technology is available, but the financial situation makes this a vanity project for people like me who can afford it, instead of what it could be: a feasible plan to get our national carbon usage down, fast.&lt;&#x2F;p&gt;
&lt;p&gt;On a positive note, our house is nice and warm, and I feel a bit less guilty about how much carbon we&#x27;re using to keep it cosy.&lt;&#x2F;p&gt;
&lt;p&gt;[Update: this was written in January 2022, and we live in South East England.]&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Providing MapLibre-compatible style JSON from openstreetmap-tile-server</title>
          <pubDate>Tue, 14 Dec 2021 16:33:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/12/14/providing-maplibre-compatible-style-json-from-openstreetmap-tile-server/</link>
          <guid>https://artificialworlds.net/blog/2021/12/14/providing-maplibre-compatible-style-json-from-openstreetmap-tile-server/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/12/14/providing-maplibre-compatible-style-json-from-openstreetmap-tile-server/">&lt;p&gt;[Previous: &lt;a href=&quot;&#x2F;blog&#x2F;2021&#x2F;12&#x2F;14&#x2F;self-hosting-maps-on-my-laptop&#x2F;&quot;&gt;Self-hosting maps on my laptop&lt;&#x2F;a&gt;]&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;a href=&quot;&#x2F;blog&#x2F;2021&#x2F;12&#x2F;14&#x2F;self-hosting-maps-on-my-laptop&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; I showed how to run &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Overv&#x2F;openstreetmap-tile-server&quot;&gt;OSM tile server stack&lt;&#x2F;a&gt; locally.&lt;&#x2F;p&gt;
&lt;p&gt;Now I&#x27;ve managed to connect a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;maplibre.org&#x2F;maplibre-gl-js-docs&#x2F;api&#x2F;&quot;&gt;MapLibre GL JS&lt;&#x2F;a&gt; front end to my local tile server and it&#x27;s showing maps!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;&lt;source src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;maplibre-connecting-to-local-tiles.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;&lt;source src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;maplibre-connecting-to-local-tiles.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;Sharing a location in Element Web&lt;&#x2F;video&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(It&#x27;s running inside &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;app.element.io&#x2F;&quot;&gt;Element Web&lt;&#x2F;a&gt;, the awesome &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; messenger I am working on. &lt;strong&gt;NOTE: this is a very, very early prototype!&lt;&#x2F;strong&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;In the previous post I ran a docker run command to launch the tile server.&lt;&#x2F;p&gt;
&lt;p&gt;This time, I had to create a file style.json:&lt;&#x2F;p&gt;
&lt;pre&gt;{
  &quot;version&quot;: 8,
  &quot;sources&quot;: {
    &quot;localsource&quot;: {
      &quot;type&quot;: &quot;raster&quot;,
      &quot;tiles&quot;: [
        &quot;http:&#x2F;&#x2F;127.0.0.1:8080&#x2F;tile&#x2F;{z}&#x2F;{x}&#x2F;{y}.png&quot;
      ],
      &quot;tileSize&quot;: 256,
      &quot;attribution&quot;: &quot;Maps Copyright 2018 &amp;lt;a href=\&quot;http:&#x2F;&#x2F;www.geofabrik.de&#x2F;\&quot;&amp;gt;Geofabrik GmbH&amp;lt;&#x2F;a&amp;gt; and &amp;lt;a href=\&quot;http:&#x2F;&#x2F;www.openstreetmap.org&#x2F;\&quot;&amp;gt;OpenStreetMap Contributors&amp;lt;&#x2F;a&amp;gt;&quot;
    }
  },
  &quot;layers&quot;: [
    {
      &quot;id&quot;: &quot;locallayer&quot;,
      &quot;source&quot;: &quot;localsource&quot;,
      &quot;type&quot;: &quot;raster&quot;
    }
  ]
}&lt;&#x2F;pre&gt;
&lt;p&gt;and then I launched the tile server with that file available in the document root:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run \
    -p 8080:80 \
    &lt;strong&gt;-v $PWD&#x2F;style.json:&#x2F;var&#x2F;www&#x2F;html&#x2F;style.json \&lt;&#x2F;strong&gt;
    -v openstreetmap-data:&#x2F;var&#x2F;lib&#x2F;postgresql&#x2F;12&#x2F;main \
    -v openstreetmap-rendered-tiles:&#x2F;var&#x2F;lib&#x2F;mod_tile \
    -e THREADS=24 \
    -e ALLOW_CORS=enabled \
    -d overv&#x2F;openstreetmap-tile-server:1.3.10 \
    run&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can point my MapLibre GL JS at that style file with code something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;this.map = new maplibregl.Map({
    container: my_container,
    style: &quot;http:&#x2F;&#x2F;127.0.0.1:8080&#x2F;style.json&quot;,
    center: [0, 0],
    zoom: 13,
});&lt;&#x2F;pre&gt;
&lt;p&gt;Very excited to be drawing maps without any requests leaving my machine!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Self-hosting maps on my laptop</title>
          <pubDate>Tue, 14 Dec 2021 13:44:57 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/12/14/self-hosting-maps-on-my-laptop/</link>
          <guid>https://artificialworlds.net/blog/2021/12/14/self-hosting-maps-on-my-laptop/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/12/14/self-hosting-maps-on-my-laptop/">&lt;p&gt;[See also: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2021&#x2F;12&#x2F;14&#x2F;providing-maplibre-compatible-style-json-from-openstreetmap-tile-server&#x2F;&quot;&gt;Providing MapLibre-compatible style JSON from openstreetmap-tile-server&lt;&#x2F;a&gt;]&lt;&#x2F;p&gt;
&lt;p&gt;As part of my research for working on location sharing for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;app.element.io&#x2F;&quot;&gt;Element Web&lt;&#x2F;a&gt;, the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt;-based instant messenger, I have been learning about tile servers.&lt;&#x2F;p&gt;
&lt;p&gt;I managed to get &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Overv&#x2F;openstreetmap-tile-server&quot;&gt;OSM tile server stack&lt;&#x2F;a&gt; working on my laptop:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;&lt;source src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;openstreetmap-tile-server.webm&quot; type=&quot;video&#x2F;webm&quot;&gt;&lt;source src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;openstreetmap-tile-server.mp4&quot; type=&quot;video&#x2F;mp4&quot;&gt;Video of a map zooming in and out with network requests fetching lots of PNG files.&lt;&#x2F;video&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here are a couple useful pages I read during my research:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;switch2osm.org&#x2F;the-basics&#x2F;&quot;&gt;Switch2OSM - The Basics&lt;&#x2F;a&gt; - explanation of some of the concepts in a way I could understand.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;switch2osm.org&#x2F;serving-tiles&#x2F;using-a-docker-container&#x2F;&quot;&gt;Switch2OSM - serving tiles using a Docker container&lt;&#x2F;a&gt; - good explanation of how to do what I write up below.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Overv&#x2F;openstreetmap-tile-server&#x2F;blob&#x2F;master&#x2F;README.md&quot;&gt;openstreetmap-tile-server README&lt;&#x2F;a&gt; - much more detail than this blog post.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;addons.mozilla.org&#x2F;en-US&#x2F;firefox&#x2F;addon&#x2F;location-guard&#x2F;&quot;&gt;Location Guard Firefox Plugin&lt;&#x2F;a&gt; - allows setting a hard-coded location instead of sharing your real location.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.jwestman.net&#x2F;2021&#x2F;11&#x2F;08&#x2F;implementing-vector-tile-support-in-libshumate.html&quot;&gt;Implementing Vector Tile Support in Libshumate&lt;&#x2F;a&gt; - explanation of the ideas from James Westman&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Today I managed to run a real tile server on my laptop, using data downloaded from OpenStreetMap in a way that I think complies with their terms of use.&lt;&#x2F;p&gt;
&lt;p&gt;To run these commands you will need &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;get-started&quot;&gt;Docker&lt;&#x2F;a&gt;, and hopefully nothing much else.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;download-the-data&quot;&gt;Download the data&lt;&#x2F;h2&gt;
&lt;p&gt;I downloaded the UK data like this:&lt;&#x2F;p&gt;
&lt;pre&gt;wget &#x27;https:&#x2F;&#x2F;download.geofabrik.de&#x2F;europe&#x2F;great-britain-latest.osm.pbf&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;You can find downloads for other regions at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;download.geofabrik.de&#x2F;&quot;&gt;download.geofabrik.de&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;import-it&quot;&gt;Import it&lt;&#x2F;h2&gt;
&lt;p&gt;Then I ran an import, which converts the PBF data into tiles that can be shown in a UI:&lt;&#x2F;p&gt;
&lt;pre&gt;docker volume create openstreetmap-data
docker volume create openstreetmap-rendered-tiles
docker run \
    -v $PWD&#x2F;great-britain-latest.osm.pbf:&#x2F;data.osm.pbf \
    -v openstreetmap-data:&#x2F;var&#x2F;lib&#x2F;postgresql&#x2F;12&#x2F;main \
    -v openstreetmap-rendered-tiles:&#x2F;var&#x2F;lib&#x2F;mod_tile \
    -e THREADS=24 \
    overv&#x2F;openstreetmap-tile-server:1.3.10 \
    import&lt;&#x2F;pre&gt;
&lt;p&gt;(Change &quot;great-britain&quot; to match what you downloaded.)&lt;&#x2F;p&gt;
&lt;p&gt;On my quite powerful laptop this took 39 minutes to run.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;run-the-tile-server&quot;&gt;Run the tile server&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, I launched the server:&lt;&#x2F;p&gt;
&lt;p&gt;(Make sure you&#x27;ve done the &quot;Import it&quot; step first.)&lt;&#x2F;p&gt;
&lt;pre&gt;docker run \
    -p 8080:80 \
    -v openstreetmap-data:&#x2F;var&#x2F;lib&#x2F;postgresql&#x2F;12&#x2F;main \
    -v openstreetmap-rendered-tiles:&#x2F;var&#x2F;lib&#x2F;mod_tile \
    -e THREADS=24 \
    -e ALLOW_CORS=enabled \
    -d overv&#x2F;openstreetmap-tile-server:1.3.10 \
    run&lt;&#x2F;pre&gt;
&lt;p&gt;This should launch the docker container in the background, which you can check with docker ps.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;test-it&quot;&gt;Test it&lt;&#x2F;h2&gt;
&lt;p&gt;You can now grab a single file by going to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;127.0.0.1:8080&#x2F;tile&#x2F;0&#x2F;0&#x2F;0.png&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:8080&#x2F;tile&#x2F;0&#x2F;0&#x2F;0.png&lt;&#x2F;a&gt;, or interact with the map properly at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;127.0.0.1:8080&quot;&gt;http:&#x2F;&#x2F;127.0.0.1:8080&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It was quite unresponsive at first, but once it had cached the tiles I was looking at, it was very smooth.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Streaming to Twitch and PeerTube simultaneously using nginx on Oracle cloud</title>
          <pubDate>Mon, 22 Nov 2021 12:33:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/11/22/streaming-to-twitch-and-peertube-simultaneously-using-nginx-on-oracle-cloud/</link>
          <guid>https://artificialworlds.net/blog/2021/11/22/streaming-to-twitch-and-peertube-simultaneously-using-nginx-on-oracle-cloud/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/11/22/streaming-to-twitch-and-peertube-simultaneously-using-nginx-on-oracle-cloud/">&lt;h2 id=&quot;simulcasting-rtmp-using-nginx&quot;&gt;Simulcasting RTMP using NGINX&lt;&#x2F;h2&gt;
&lt;p&gt;I want people to be able to watch my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;c&#x2F;andybalaam_live&#x2F;videos&quot;&gt;Matrix and Rust live coding streams&lt;&#x2F;a&gt; using free software, so I&#x27;d like to simulcast to PeerTube as well as Twitch.&lt;&#x2F;p&gt;
&lt;p&gt;This is possible using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.nginx.com&#x2F;&quot;&gt;NGINX&lt;&#x2F;a&gt; and its &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arut&#x2F;nginx-rtmp-module&quot;&gt;RTMP module&lt;&#x2F;a&gt;. It does involve building NGINX from source, but I actually found that reasonably easy to do.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-oracle-cloud&quot;&gt;Why Oracle cloud?&lt;&#x2F;h2&gt;
&lt;p&gt;I would never recommend using Oracle for anything, but they do provide up to two virtual machines in their cloud for free, and the one I am using has been consistently available with very good connectivity, in a London data centre since I set it up several months ago.&lt;&#x2F;p&gt;
&lt;p&gt;So, we are making our lives more difficult by trying to do this on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Oracle_Linux&quot;&gt;Oracle Linux&lt;&#x2F;a&gt;, which is a derivative of RHEL.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-nginx-and-its-rtmp-module-on-oracle-linux&quot;&gt;Building NGINX and its RTMP module on Oracle Linux&lt;&#x2F;h2&gt;
&lt;p&gt;I ran these commands on my Oracle cloud instance (running Oracle Linux):&lt;&#x2F;p&gt;
&lt;pre&gt;sudo yum install git pcre-devel openssl-devel
mkdir nginx
cd nginx
wget http:&#x2F;&#x2F;nginx.org&#x2F;download&#x2F;nginx-1.21.4.tar.gz
git clone https:&#x2F;&#x2F;github.com&#x2F;arut&#x2F;nginx-rtmp-module.git
cd nginx-1.21.4
.&#x2F;configure --add-module=..&#x2F;nginx-rtmp-module&#x2F;
make
sudo make install&lt;&#x2F;pre&gt;
&lt;p&gt;After all this NGINX was installed to &#x2F;usr&#x2F;local&#x2F;nginx&#x2F;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-the-nginx-config-file-for-rtmp-simulcasting&quot;&gt;Creating the NGINX config file for RTMP simulcasting&lt;&#x2F;h2&gt;
&lt;p&gt;Next I edited the NGINX config file by typing:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo nano &#x2F;usr&#x2F;local&#x2F;nginx&#x2F;conf&#x2F;nginx.conf&lt;&#x2F;pre&gt;
&lt;p&gt;And pasted in this config at the bottom of the file:&lt;&#x2F;p&gt;
&lt;pre&gt;rtmp {
    server {
        listen 2036;
        chunk_size 4096;
        application live {
            live on;
            record off;
            push rtmp:&#x2F;&#x2F;live.twitch.tv&#x2F;app&#x2F;live_INSERT_TWITCH_STREAM_KEY;
            push rtmp:&#x2F;&#x2F;diode.zone&#x2F;live&#x2F;INSERT_PEERTUBE_STREAM_KEY;
        }
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that you will need to get your Twitch stream key from Twitch -&amp;gt; Creator Dashboard -&amp;gt; Settings -&amp;gt; Stream, then Copy next to the Primary Stream Key.&lt;&#x2F;p&gt;
&lt;p&gt;To get a PeerTube stream ID, you will need to go to your PeerTube page and click Publish, then Go Live, choose your channel and choose Go Live. Note that if you want the streams to record and be available later, you have to create a new stream key each time you start a stream, and change it in nginx.conf.&lt;&#x2F;p&gt;
&lt;p&gt;If you use a different PeerTube server (I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;diode.zone&#x2F;&quot;&gt;diode.zone&lt;&#x2F;a&gt;) then you&#x27;ll need to change the server name in the config file above too.&lt;&#x2F;p&gt;
&lt;p&gt;Make sure your config file is saved with the right URLs in it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;opening-ports&quot;&gt;Opening ports&lt;&#x2F;h2&gt;
&lt;p&gt;To send RTMP traffic to my server, I needed to open the right port to the Oracle cloud instance. That involved creating an ingress rule, and adding a firewall rule.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-an-ingress-rule&quot;&gt;Creating an ingress rule&lt;&#x2F;h3&gt;
&lt;p&gt;In the web interface, I went to the menu in the top left, clicked Compute, then Instances.&lt;&#x2F;p&gt;
&lt;p&gt;I clicked on my instance&#x27;s name, then I clicked on the name of the subnet in the details (on the right).&lt;&#x2F;p&gt;
&lt;p&gt;I clicked on Default security list for..., then Add Ingress Rules.&lt;&#x2F;p&gt;
&lt;p&gt;I made an ingress rule with Source Type=CIDR, Source CIDR=0.0.0.0&#x2F;0, IP Protocol=TCP, Source Port Range=(blank, meaning all), Destination Port Range=2036&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-a-firewall-rule&quot;&gt;Adding a firewall rule&lt;&#x2F;h3&gt;
&lt;p&gt;Then I ssh&#x27;d into the machine and ran these commands to create a firewall rule allowing the traffic:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo firewall-cmd --zone=public --permanent --add-port=2036&#x2F;tcp
sudo firewall-cmd --reload&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;stop-and-start-nginx&quot;&gt;Stop and Start NGINX&lt;&#x2F;h2&gt;
&lt;p&gt;After creating the config file and opening the right port, I needed to start NGINX.&lt;&#x2F;p&gt;
&lt;p&gt;Every time I change the config file, I need to restart it.&lt;&#x2F;p&gt;
&lt;p&gt;If it&#x27;s already running, I stop it with:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo &#x2F;usr&#x2F;local&#x2F;nginx&#x2F;sbin&#x2F;nginx -s stop&lt;&#x2F;pre&gt;
&lt;p&gt;and then I start it up again with&lt;&#x2F;p&gt;
&lt;pre&gt;sudo &#x2F;usr&#x2F;local&#x2F;nginx&#x2F;sbin&#x2F;nginx&lt;&#x2F;pre&gt;
&lt;p&gt;I can check whether it&#x27;s happy by looking at the log files, for example to see any errors:&lt;&#x2F;p&gt;
&lt;pre&gt;less &#x2F;usr&#x2F;local&#x2F;nginx&#x2F;logs&#x2F;error.log&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;starting-the-stream&quot;&gt;Starting the stream&lt;&#x2F;h2&gt;
&lt;p&gt;Now I go into OBS and go to File -&amp;gt; Settings -&amp;gt; Stream and choose the type as Custom, and the Server as rtmp:&#x2F;&#x2F;1.1.1.1:2036&#x2F;live. (But instead of 1.1.1.1 I put the public IP address of my instance, which I found by clicking the name of the instance in the Oracle cloud management console.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New game: Tron - frantic multiplayer retro action</title>
          <pubDate>Fri, 19 Nov 2021 02:46:31 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/11/19/new-game-tron-frantic-multiplayer-retro-action/</link>
          <guid>https://artificialworlds.net/blog/2021/11/19/new-game-tron-frantic-multiplayer-retro-action/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/11/19/new-game-tron-frantic-multiplayer-retro-action/">&lt;p&gt;My newest game is out now on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl Games&lt;&#x2F;a&gt; - Tron:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;tron.gif&quot; alt=&quot;Pixellated lines fight each other to stay alive&quot; width=&quot;400&quot; height=&quot;422&quot; class=&quot;aligncenter size-full wp-image-3332&quot;&gt;
&lt;p&gt;Play at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.gitlab.io&#x2F;tron&#x2F;&quot;&gt;smolpxl.gitlab.io&#x2F;tron&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s a frantic multiplayer retro pixellated thingy playable in your browser. Try to stay alive longer than everyone else!&lt;&#x2F;p&gt;
&lt;p&gt;This version allows many players (up to 16 if you can manage it), and is quite pure in its implementation.&lt;&#x2F;p&gt;
&lt;p&gt;There are bots to play against, and you can gather your friends around a keyboard to play together.&lt;&#x2F;p&gt;
&lt;p&gt;Part of the motivation for writing this game was to test my new smolpxl-remote remote-play system, but this is not enabled yet, so watch this space...&lt;&#x2F;p&gt;
&lt;p&gt;I love playing games with other people - preferably at least 3 other people. In theory you could have 8 players around a keyboard playing this - send me a picture if you try!&lt;&#x2F;p&gt;
&lt;p&gt;One feature I worked on in the Smolpxl library for this game: saving configuration to local storage (and asking permission to do so). I ended up with a very ugly hack to do this, so a bit more work is needed before I merge it into the library.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Preventing Virgin Media hijacking my DNS</title>
          <pubDate>Wed, 17 Nov 2021 09:33:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/11/17/preventing-virgin-media-hijacking-my-dns/</link>
          <guid>https://artificialworlds.net/blog/2021/11/17/preventing-virgin-media-hijacking-my-dns/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/11/17/preventing-virgin-media-hijacking-my-dns/">&lt;p&gt;Yesterday I learned that Virgin Media is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;VirginMedia&#x2F;comments&#x2F;g0spb4&#x2F;virgin_media_is_hijacking_our_connections&#x2F;&quot;&gt;inserting itself into some of my DNS requests&lt;&#x2F;a&gt;. Much as I am not a fan of how powerful Cloudflare are, if they are telling the truth about their DNS, then it&#x27;s safe, so I followed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;1.1.1.1&#x2F;setup-1.1.1.1&#x2F;linux&quot;&gt;their instructions on how to use their DNS&lt;&#x2F;a&gt; and then &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serverfault.com&#x2F;a&#x2F;810639&#x2F;24813&quot;&gt;removed the default DNS&lt;&#x2F;a&gt; and hopefully my Internet will work now.&lt;&#x2F;p&gt;
&lt;p&gt;From the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serverfault.com&#x2F;a&#x2F;810639&#x2F;24813&quot;&gt;serverfault answer by lauc.exon.nod&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;nmcli con mod &quot;Wired connection 1&quot; ipv4.dns &quot;1.1.1.1 1.0.0.1&quot;
nmcli con mod &quot;Wired connection 1&quot; ipv4.ignore-auto-dns yes
nmcli con down &quot;Wired connection 1&quot;
nmcli con up &quot;Wired connection 1&quot;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP about climate emergency</title>
          <pubDate>Fri, 15 Oct 2021 10:16:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/10/15/letter-to-my-mp-about-climate-emergency/</link>
          <guid>https://artificialworlds.net/blog/2021/10/15/letter-to-my-mp-about-climate-emergency/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/10/15/letter-to-my-mp-about-climate-emergency/">&lt;p&gt;[Introduction including details about my own air source heat pump install, and mention of the ending of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gov.uk&#x2F;domestic-renewable-heat-incentive&quot;&gt;RHI&lt;&#x2F;a&gt; funding in April 2022.]&lt;&#x2F;p&gt;
&lt;p&gt;After I have installed an air source heat pump, I will pay more money to heat my home, even though I am using less energy, because electricity is more expensive than gas. So this change will hurt me financially over both the short and longer terms.&lt;&#x2F;p&gt;
&lt;p&gt;Do you agree with me that climate emergency is the most important issue the government is now facing?&lt;&#x2F;p&gt;
&lt;p&gt;Do you also agree with me that we urgently need people to switch their heating and home insulation to reduce our dependence on burning gas?&lt;&#x2F;p&gt;
&lt;p&gt;Please do all you can to persuade the Prime Minister to introduce initiatives before COP26 that make it financially viable for families without spare cash to insulate their home and heat them with renewable energy.&lt;&#x2F;p&gt;
&lt;p&gt;Please pass my letter on to the Prime Minister and any government departments you consider relevant.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you very much for your time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New Job at Element (Matrix)</title>
          <pubDate>Mon, 27 Sep 2021 15:27:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/09/27/new-job-at-element-matrix/</link>
          <guid>https://artificialworlds.net/blog/2021/09/27/new-job-at-element-matrix/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/09/27/new-job-at-element-matrix/">&lt;p&gt;I started a new job today at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;element.io&#x2F;&quot;&gt;Element&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;It has been a long-standing ambition of mine to work in Free and Open Source software, and I am very excited to work for a company that is the main developer of a really important project: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;&quot;&gt;Matrix&lt;&#x2F;a&gt; communication network.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know much about what I&#x27;ll be doing yet, but finding an open source company with a decent business model that is prepared to pay me is very exciting. The fact that they have offices that are close enough for me to go for is another huge bonus.&lt;&#x2F;p&gt;
&lt;p&gt;Wish me luck, and I&#x27;ll let you know what I&#x27;m working on when it becomes more clear.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What to cache when building Rust using Gitlab CI or similar</title>
          <pubDate>Thu, 16 Sep 2021 01:00:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/09/16/what-to-cache-when-building-rust-using-gitlab-ci-or-similar/</link>
          <guid>https://artificialworlds.net/blog/2021/09/16/what-to-cache-when-building-rust-using-gitlab-ci-or-similar/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/09/16/what-to-cache-when-building-rust-using-gitlab-ci-or-similar/">&lt;p&gt;&lt;strong&gt;Update: caching $RUSTUP_HOME and $CARGO_HOME does not work for me - I removed them.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;When building your project with Gitlab CI or a similar build tool, you can end up spending a lot of time watching your build repeat the same steps over and over. This is especially frustrating when it mostly consists of downloading and compiling the same things we downloaded and compiled last time.&lt;&#x2F;p&gt;
&lt;p&gt;To mitigate this, we can ask Gitlab CI to cache things that will be the same next time.&lt;&#x2F;p&gt;
&lt;p&gt;For a Rust project, the most important thing to cache is target in the local directory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: nope, incorrect&lt;&#x2F;strong&gt; But, if you are installing tools using rustup or cargo, it will really help if you cache those too. Fortunately, Rust knows where those are by using environment variables, and these are defined in the standard Rust Docker image.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: if anyone knows how to cache the output of rustup and cargo installations, let me know. I think I would need to push a Docker image to Dockerhub to do it?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We can make sure we&#x27;re caching as much as possible by adding a section like this to .gitlab-ci.yml:&lt;&#x2F;p&gt;
&lt;pre&gt;    cache:
        key: shared-cache
        paths:
            - target&#x2F;&lt;&#x2F;pre&gt;
&lt;p&gt;If you add this to all your jobs, they will share a single cache between them, and cache the local target directory as well as any tools installed with rustup or cargo.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a full example from my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;evolve-svgs&quot;&gt;Evolve SVGs&lt;&#x2F;a&gt; project:&lt;&#x2F;p&gt;
&lt;pre&gt;image: rust:latest

before_script:
    - rustup component add rustfmt
    - rustup target add wasm32-unknown-unknown
    - cargo install trunk wasm-bindgen-cli

pages:
    stage: deploy
    script:
        - echo &quot;Publishing pages to&quot; $CI_PAGES_URL
        - make deploy
        - mv dist public
    artifacts:
      paths:
        - public
    only:
        - main
    cache:
        key: shared-cache
        paths:
            - target&#x2F;

test:
    stage: test
    script:
        - make test
    cache:
        key: shared-cache
        paths:
            - target&#x2F;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Printing the version of a Maven project</title>
          <pubDate>Wed, 15 Sep 2021 10:03:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/09/15/printing-the-version-of-a-maven-project/</link>
          <guid>https://artificialworlds.net/blog/2021/09/15/printing-the-version-of-a-maven-project/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/09/15/printing-the-version-of-a-maven-project/">&lt;p&gt;To print a Maven project&#x27;s version number, try:&lt;&#x2F;p&gt;
&lt;pre&gt;mvn help:evaluate -Dexpression=project.version -q -DforceStdout&lt;&#x2F;pre&gt;
&lt;p&gt;-q suppresses all output(!)&lt;&#x2F;p&gt;
&lt;p&gt;-DforceStdout unsuppresses the actual thing you asked to be printed.&lt;&#x2F;p&gt;
&lt;p&gt;I will refrain from commenting on how difficult this was to find.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Minimal example of a Maven pom for a mixed Kotlin and Java project</title>
          <pubDate>Wed, 18 Aug 2021 10:45:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/08/18/minimal-example-of-a-maven-pom-for-a-mixed-kotlin-and-java-project/</link>
          <guid>https://artificialworlds.net/blog/2021/08/18/minimal-example-of-a-maven-pom-for-a-mixed-kotlin-and-java-project/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/08/18/minimal-example-of-a-maven-pom-for-a-mixed-kotlin-and-java-project/">&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kotlinlang.org&#x2F;docs&#x2F;maven.html&quot;&gt;Kotlin docs&lt;&#x2F;a&gt; describe some things you need in your pom.xml to create a project that is a mix of Kotlin and Java code, but there is no complete example, so here is mine:&lt;&#x2F;p&gt;
&lt;p&gt;pom.xml:&lt;pre&gt;&amp;lt;project&amp;gt;
&amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;&#x2F;modelVersion&amp;gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;lt;groupId&amp;amp;gt;com.example.kj&amp;amp;lt;&#x2F;groupId&amp;amp;gt;
&amp;amp;lt;artifactId&amp;amp;gt;kotlin-and-java&amp;amp;lt;&#x2F;artifactId&amp;amp;gt;
&amp;amp;lt;version&amp;amp;gt;1.0.0-SNAPSHOT&amp;amp;lt;&#x2F;version&amp;amp;gt;

&amp;amp;lt;properties&amp;amp;gt;
    &amp;amp;lt;kotlin.version&amp;amp;gt;1.5.21&amp;amp;lt;&#x2F;kotlin.version&amp;amp;gt;
    &amp;amp;lt;project.build.sourceEncoding&amp;amp;gt;UTF-8&amp;amp;lt;&#x2F;project.build.sourceEncoding&amp;amp;gt;
    &amp;amp;lt;project.reporting.outputEncoding&amp;amp;gt;UTF-8&amp;amp;lt;&#x2F;project.reporting.outputEncoding&amp;amp;gt;
&amp;amp;lt;&#x2F;properties&amp;amp;gt;

&amp;amp;lt;build&amp;amp;gt;
    &amp;amp;lt;plugins&amp;amp;gt;
        &amp;amp;lt;plugin&amp;amp;gt;
            &amp;amp;lt;groupId&amp;amp;gt;org.jetbrains.kotlin&amp;amp;lt;&#x2F;groupId&amp;amp;gt;
            &amp;amp;lt;artifactId&amp;amp;gt;kotlin-maven-plugin&amp;amp;lt;&#x2F;artifactId&amp;amp;gt;
            &amp;amp;lt;version&amp;amp;gt;${kotlin.version}&amp;amp;lt;&#x2F;version&amp;amp;gt;
            &amp;amp;lt;executions&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;compile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt;
                        &amp;amp;lt;goal&amp;amp;gt;compile&amp;amp;lt;&#x2F;goal&amp;amp;gt;
                    &amp;amp;lt;&#x2F;goals&amp;amp;gt;
                    &amp;amp;lt;configuration&amp;amp;gt;
                        &amp;amp;lt;sourceDirs&amp;amp;gt;
                            &amp;amp;lt;sourceDir&amp;amp;gt;${project.basedir}&#x2F;src&#x2F;main&#x2F;kotlin&amp;amp;lt;&#x2F;sourceDir&amp;amp;gt;
                            &amp;amp;lt;sourceDir&amp;amp;gt;${project.basedir}&#x2F;src&#x2F;main&#x2F;java&amp;amp;lt;&#x2F;sourceDir&amp;amp;gt;
                        &amp;amp;lt;&#x2F;sourceDirs&amp;amp;gt;
                    &amp;amp;lt;&#x2F;configuration&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;test-compile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt; &amp;amp;lt;goal&amp;amp;gt;test-compile&amp;amp;lt;&#x2F;goal&amp;amp;gt; &amp;amp;lt;&#x2F;goals&amp;amp;gt;
                    &amp;amp;lt;configuration&amp;amp;gt;
                        &amp;amp;lt;sourceDirs&amp;amp;gt;
                            &amp;amp;lt;sourceDir&amp;amp;gt;${project.basedir}&#x2F;src&#x2F;test&#x2F;kotlin&amp;amp;lt;&#x2F;sourceDir&amp;amp;gt;
                            &amp;amp;lt;sourceDir&amp;amp;gt;${project.basedir}&#x2F;src&#x2F;test&#x2F;java&amp;amp;lt;&#x2F;sourceDir&amp;amp;gt;
                        &amp;amp;lt;&#x2F;sourceDirs&amp;amp;gt;
                    &amp;amp;lt;&#x2F;configuration&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
            &amp;amp;lt;&#x2F;executions&amp;amp;gt;
        &amp;amp;lt;&#x2F;plugin&amp;amp;gt;
        &amp;amp;lt;plugin&amp;amp;gt;
            &amp;amp;lt;groupId&amp;amp;gt;org.apache.maven.plugins&amp;amp;lt;&#x2F;groupId&amp;amp;gt;
            &amp;amp;lt;artifactId&amp;amp;gt;maven-compiler-plugin&amp;amp;lt;&#x2F;artifactId&amp;amp;gt;
            &amp;amp;lt;version&amp;amp;gt;3.5.1&amp;amp;lt;&#x2F;version&amp;amp;gt;
            &amp;amp;lt;executions&amp;amp;gt;
                &amp;amp;lt;!-- Replacing default-compile as it is treated specially by maven --&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;default-compile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;phase&amp;amp;gt;none&amp;amp;lt;&#x2F;phase&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
                &amp;amp;lt;!-- Replacing default-testCompile as it is treated specially by maven --&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;default-testCompile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;phase&amp;amp;gt;none&amp;amp;lt;&#x2F;phase&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;java-compile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;phase&amp;amp;gt;compile&amp;amp;lt;&#x2F;phase&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt;
                        &amp;amp;lt;goal&amp;amp;gt;compile&amp;amp;lt;&#x2F;goal&amp;amp;gt;
                    &amp;amp;lt;&#x2F;goals&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
                &amp;amp;lt;execution&amp;amp;gt;
                    &amp;amp;lt;id&amp;amp;gt;java-test-compile&amp;amp;lt;&#x2F;id&amp;amp;gt;
                    &amp;amp;lt;phase&amp;amp;gt;test-compile&amp;amp;lt;&#x2F;phase&amp;amp;gt;
                    &amp;amp;lt;goals&amp;amp;gt;
                        &amp;amp;lt;goal&amp;amp;gt;testCompile&amp;amp;lt;&#x2F;goal&amp;amp;gt;
                    &amp;amp;lt;&#x2F;goals&amp;amp;gt;
                &amp;amp;lt;&#x2F;execution&amp;amp;gt;
            &amp;amp;lt;&#x2F;executions&amp;amp;gt;
        &amp;amp;lt;&#x2F;plugin&amp;amp;gt;
    &amp;amp;lt;&#x2F;plugins&amp;amp;gt;
&amp;amp;lt;&#x2F;build&amp;amp;gt;
&amp;amp;lt;dependencies&amp;amp;gt;
    &amp;amp;lt;dependency&amp;amp;gt;
        &amp;amp;lt;groupId&amp;amp;gt;org.jetbrains.kotlin&amp;amp;lt;&#x2F;groupId&amp;amp;gt;
        &amp;amp;lt;artifactId&amp;amp;gt;kotlin-stdlib&amp;amp;lt;&#x2F;artifactId&amp;amp;gt;
        &amp;amp;lt;version&amp;amp;gt;${kotlin.version}&amp;amp;lt;&#x2F;version&amp;amp;gt;
    &amp;amp;lt;&#x2F;dependency&amp;amp;gt;
&amp;amp;lt;&#x2F;dependencies&amp;amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&amp;lt;&#x2F;project&amp;gt;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;src&#x2F;main&#x2F;java&#x2F;MyJava.java:&lt;pre&gt;public class MyJava {
public static void main(String[] args) {
MyKotlin k = new MyKotlin();  &#x2F;&#x2F; Use Kotlin from Java
System.out.println(k.message());
}
}&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;src&#x2F;main&#x2F;kotlin&#x2F;MyKotlin.kt:&lt;pre&gt;class MyKotlin : MyJava() {  &#x2F;&#x2F; Use Java from Kotlin
fun message(): String {
return &quot;Hello from Kotlin!&quot;
}
}&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;src&#x2F;test&#x2F;java&#x2F;MadeInJavaTest.java:&lt;pre&gt;class MadeInJavaTest {
public void testCanUseJava() {
MyJava j = new MyJava();
}&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;public void testCanUseKotlin() {
    MyKotlin k = new MyKotlin();
    assertEquals(k.message(), &amp;quot;Hello from Kotlin!&amp;quot;);
}

static void assertEquals(String left, String right) {
    if (!left.equals(right)) {
        throw new AssertionError(left + &amp;quot; != &amp;quot; + right);
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;src&#x2F;test&#x2F;kotlin&#x2F;MadeInKotlinTest.kt:&lt;pre&gt;class MadeInKotlinTest {
fun testCanUseJava() {
MyJava()
}&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;fun testCanUseKotlin() {
    val k = MyKotlin();
    assertEquals(k.message(), &amp;quot;Hello from Kotlin!&amp;quot;);
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}&lt;&#x2F;p&gt;
&lt;p&gt;fun assertEquals(left: String, right: String) {
if (left != right) {
throw AssertionError(&quot;${left} != ${right}&quot;);
}
}&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Importing&#x2F;migrating from one peertube server to another</title>
          <pubDate>Fri, 06 Aug 2021 16:04:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/08/06/importing-migrating-from-one-peertube-server-to-another/</link>
          <guid>https://artificialworlds.net/blog/2021/08/06/importing-migrating-from-one-peertube-server-to-another/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/08/06/importing-migrating-from-one-peertube-server-to-another/">&lt;p&gt;My Peertube server is shutting down, so I need to move my videos to another one. The official scripts don&#x27;t seem to cover this case very well, so here is what I did.&lt;&#x2F;p&gt;
&lt;p&gt;My script fetches videos and their details and uploads them to the new server via the Peertube API.&lt;&#x2F;p&gt;
&lt;p&gt;Contributions welcome: I was not able to copy video descriptions across, and I was too lazy so I hard-coded the number of tags. Also, I didn&#x27;t make a git repo for all this because I felt it needs more thought, but feel free to start one and I will happily contribute this.&lt;&#x2F;p&gt;
&lt;p&gt;This script copies all videos in a single Peertube channel to a different server. You must find the numeric ID of the channel on the new server to copy into, which I did by looking at the responses in the Network tab of Firefox&#x27;s developer tools when I clicked on its name in the web interface. It requires bash, curl, youtube-dl and jq.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s peertube-migrate-channel.bash:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -u
set -e

# Modify these for your setup
OLD_SERVER=&quot;INSERT OLD SERVER e.g. https:&#x2F;&#x2F;peertube.social&quot;
OLD_CHANNEL=&quot;INSERT CHANNEL URL-NAME e.g. trials_fusion&quot;
NEW_SERVER=&quot;INSERT NEW SERVER e.g. https:&#x2F;&#x2F;video.hardlimit.com&quot;
NEW_CHANNEL=&quot;INSERT NEW CHANNEL ID e.g. 4103&quot;
USERNAME=&quot;INSERT_USERNAME for new server e.g. trials&quot;
PASSWORD=&quot;INSERT PASSWORD for new server&quot;
TAG1=&quot;INSERT_A_TAG e.g trials&quot;
TAG2=&quot;INSERT_A_TAG e.g. gaming&quot;
TAG3=&quot;INSERT_A_TAG e.g. gameing&quot;

DIR=$(mktemp -d)
API_PATH=&quot;${NEW_SERVER}&#x2F;api&#x2F;v1&quot;

# Find out how many videos are in the channel
curl -s \
    &quot;${OLD_SERVER}&#x2F;api&#x2F;v1&#x2F;video-channels&#x2F;${OLD_CHANNEL}&#x2F;videos&#x2F;?count=1&quot; \
    &amp;gt; &quot;${DIR}&#x2F;videos-total.json&quot;

TOTAL=$(jq .total &amp;lt; &quot;${DIR}&#x2F;videos-total.json&quot;)
CURRENT=0
PAGE_SIZE=10

# Get a list of URLS for all the videos

echo -n &quot;&quot; &amp;gt; &quot;${DIR}&#x2F;urls.txt&quot;

while (( CURRENT &amp;lt; TOTAL )); do
    FILE=&quot;${DIR}&#x2F;videos-page-${CURRENT}.json&quot;

    curl -s \
        &quot;${OLD_SERVER}&#x2F;api&#x2F;v1&#x2F;video-channels&#x2F;${OLD_CHANNEL}&#x2F;videos&#x2F;?start=${CURRENT}&amp;amp;count=${PAGE_SIZE}&amp;amp;skipCount=true&quot; \
        &amp;gt; &quot;${FILE}&quot;

    jq &quot;.data | map(.uuid) | .[]&quot; -r &amp;lt; &quot;${FILE}&quot; &amp;gt;&amp;gt; &quot;${DIR}&#x2F;urls.txt&quot;

    CURRENT=$((CURRENT + PAGE_SIZE))
done

# Log in to the new server

client_id=$(curl -s &quot;${API_PATH}&#x2F;oauth-clients&#x2F;local&quot; | jq -r &quot;.client_id&quot;)
client_secret=$(curl -s &quot;${API_PATH}&#x2F;oauth-clients&#x2F;local&quot; | jq -r &quot;.client_secret&quot;)
token=$(curl -s &quot;${API_PATH}&#x2F;users&#x2F;token&quot; \
  --data client_id=&quot;${client_id}&quot; \
  --data client_secret=&quot;${client_secret}&quot; \
  --data grant_type=password \
  --data response_type=code \
  --data username=&quot;${USERNAME}&quot; \
  --data password=&quot;${PASSWORD}&quot; \
  | jq -r &quot;.access_token&quot;)

# Download and upload each video

tac &quot;${DIR}&#x2F;urls.txt&quot; \
    | while read ID; do
        URL=&quot;${OLD_SERVER}&#x2F;api&#x2F;v1&#x2F;videos&#x2F;${ID}&quot;
        curl -s &quot;${URL}&quot; &amp;gt; &quot;${DIR}&#x2F;info-${ID}.json&quot;
        NAME=$(jq -r .name &amp;lt; &quot;${DIR}&#x2F;info-${ID}.json&quot;)
        CATEGORY=$(jq -r .category.id &amp;lt; &quot;${DIR}&#x2F;info-${ID}.json&quot;)
        LICENCE=$(jq -r .licence.id &amp;lt; &quot;${DIR}&#x2F;info-${ID}.json&quot;)
        LANGUAGE=$(jq -r .language.id &amp;lt; &quot;${DIR}&#x2F;info-${ID}.json&quot;)
        PRIVACY=$(jq -r .privacy.id &amp;lt; &quot;${DIR}&#x2F;info-${ID}.json&quot;)

        OLD_VIDEO=&quot;https:&#x2F;&#x2F;peertube.social&#x2F;videos&#x2F;watch&#x2F;${ID}&quot;
	mkdir &quot;${DIR}&#x2F;dl-${ID}&quot;
	youtube-dl &quot;${OLD_VIDEO}&quot; --output=&quot;${DIR}&#x2F;dl-${ID}&#x2F;dl.mp4&quot;

        echo &quot;Uploading ${OLD_VIDEO}&quot;
        curl &quot;${API_PATH}&#x2F;videos&#x2F;upload&quot; \
	  --silent \
          -H &quot;Authorization: Bearer ${token}&quot; \
          --output &quot;${DIR}&#x2F;curl-out-${ID}.txt&quot; \
          --max-time 6000 \
	  --form name=&quot;${NAME}&quot; \
	  --form videofile=@&quot;${DIR}&#x2F;dl-${ID}&#x2F;dl.mp4&quot; \
          --form channelId=${NEW_CHANNEL} \
          --form category=${CATEGORY} \
          --form licence=${LICENCE} \
          --form description=&quot;TODO VIDEO DESCRIPTION&quot; \
          --form language=${LANGUAGE} \
          --form privacy=${PRIVACY} \
          --form tags=&quot;${TAG1}&quot; \
          --form tags=&quot;${TAG2}&quot; \
          --form tags=&quot;${TAG3}&quot;
    done
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Why write an entire game (including Graphics) in a single, hand-coded JavaScript file?</title>
          <pubDate>Thu, 01 Jul 2021 07:36:59 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/07/01/why-write-an-entire-game-including-graphics-in-a-single-hand-coded-javascript-file/</link>
          <guid>https://artificialworlds.net/blog/2021/07/01/why-write-an-entire-game-including-graphics-in-a-single-hand-coded-javascript-file/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/07/01/why-write-an-entire-game-including-graphics-in-a-single-hand-coded-javascript-file/">&lt;p&gt;My new game, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.gitlab.io&#x2F;rightwaves&#x2F;&quot;&gt;Rightwaves&lt;&#x2F;a&gt;, is out now! It&#x27;s a tribute to the great classic &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;archive.org&#x2F;details&#x2F;arcade_rtype&quot;&gt;R-Type&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.gitlab.io&#x2F;rightwaves&#x2F;&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rightwaves-level1.gif&quot; alt=&quot;A pixel-art spaceship battles aliens&quot; width=&quot;360&quot; height=&quot;200&quot; class=&quot;aligncenter size-full wp-image-3244&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The entire implementation, including all the graphics, is contained within &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;public&#x2F;game.js&quot;&gt;a single ~5000-line JavaScript file&lt;&#x2F;a&gt;. Why?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-is-a-terrible-idea&quot;&gt;This is a terrible idea&lt;&#x2F;h2&gt;
&lt;p&gt;Let me start by saying I do not recommend writing code this way. It&#x27;s a terrible idea, and some aspects of the development process have been severely hampered by doing this.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rightwaves.png&quot; alt=&quot;A pixellated spaceship dodges bullets and heads for a narrow passageway&quot; width=&quot;400&quot; height=&quot;422&quot; class=&quot;aligncenter size-full wp-image-3249&quot;&gt;
&lt;h2 id=&quot;complexity&quot;&gt;Complexity&lt;&#x2F;h2&gt;
&lt;p&gt;One of my goals for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl&lt;&#x2F;a&gt; is to strip coding back to the simplest possible thing it can be.&lt;&#x2F;p&gt;
&lt;p&gt;Over the last 40 years, programming has changed a huge amount: the number and variety of the tools we have to work with has increased beyond what I can take in, and almost all of these things are utterly brilliant.&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t imagine writing a game like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.gitlab.io&#x2F;eat-apples-quick&#x2F;&quot;&gt;Eat Apples Quick&lt;&#x2F;a&gt; using the line-orientated editor I used on the ZX Spectrum. I thoroughly enjoyed the Rust code completion and documentation I get from running &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rust-lang&#x2F;rls&quot;&gt;rust-language-server&lt;&#x2F;a&gt; inside &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;&quot;&gt;NeoVim&lt;&#x2F;a&gt;. (I&#x27;ve even heard there are editors that work inside a windowed environment, but I am not brave enough to try one.)&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;eatapplesquick.png&quot; alt=&quot;A pacman-like game&quot; width=&quot;400&quot; height=&quot;422&quot; class=&quot;aligncenter size-full wp-image-3250&quot;&gt;
&lt;p&gt;I can&#x27;t imagine writing the simple physics engine for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.gitlab.io&#x2F;smolpxl&#x2F;spring&quot;&gt;Spring&lt;&#x2F;a&gt; in assembly language, or trying to concentrate on that while I had to unravel complex graphics rendering conventions and timings. Instead, I was able to write code that looked a bit like the maths it represented, and allow the browser to handle the graphics card.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;spring.png&quot; alt=&quot;A spring with a cheery face bounces off a platform&quot; width=&quot;400&quot; height=&quot;422&quot; class=&quot;aligncenter size-full wp-image-3251&quot;&gt;
&lt;p&gt;But, along with all these powerful tools like high-level programming languages, fully cross-platform user interface components, and libraries that make writing a game loop simple, comes a new problem: complexity.&lt;&#x2F;p&gt;
&lt;p&gt;We must choose which tools, libraries and paradigms we&#x27;re going to use, and to make that choice we need to understand them.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;smolpxl&quot;&gt;Smolpxl library&lt;&#x2F;a&gt; makes some of these choices for you, by enforcing an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;-style model&#x2F;update&#x2F;view split, providing a pixellated canvas of known size, and handling a game loop with fixed framerate. This is particularly suited to writing little retro-style games, similar to what I used to write in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;retrogamecoders.com&#x2F;amos-basic-screens-images&#x2F;&quot;&gt;AMOS Basic&lt;&#x2F;a&gt; on my Amiga.&lt;&#x2F;p&gt;
&lt;p&gt;I want to make everything simple, but I want our game to work on people&#x27;s computers. The only platform we can realistically choose is the browser: the exact same code works almost everywhere.&lt;&#x2F;p&gt;
&lt;p&gt;None of this explains why I would write a game inside a single file of source code, but hopefully it begins to demonstrate where I am coming from.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simplicity&quot;&gt;Simplicity&lt;&#x2F;h2&gt;
&lt;p&gt;Another goal for Smolpxl is to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;makeagame.html&quot;&gt;teach programming&lt;&#x2F;a&gt;, and a huge barrier I see to getting started is the build process.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;no-build&quot;&gt;No build&lt;&#x2F;h3&gt;
&lt;p&gt;You can write Smolpxl games in Rust and compile them to WASM (which is how &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;eat-apples-quick&quot;&gt;Eat Apples Quick&lt;&#x2F;a&gt; is done), and you could also package the JavaScript with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;&quot;&gt;webpack&lt;&#x2F;a&gt; or similar, but I want the first-class way of using Smolpxl to involve &lt;strong&gt;no build at all&lt;&#x2F;strong&gt;: I want you to be able to copy in a standard index.html and smolpxl.js and then write your code in game.js and have it just work when you double-click on index.html.&lt;&#x2F;p&gt;
&lt;p&gt;A goal for Rightwaves is to demonstrate that you can write a full game in Smolpxl, without stepping outside that simple story: &lt;strong&gt;just double-click on index.html&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;it-s-all-inside-game-js&quot;&gt;It&#x27;s all inside game.js&lt;&#x2F;h3&gt;
&lt;p&gt;So, Rightwaves is a single* JavaScript file containing the code, the level descriptions, and, probably most unusually, the graphics.&lt;&#x2F;p&gt;
&lt;p&gt;* Note: I cheated - the &quot;action-replay&quot; data is in a separate file - it was much bigger than the source code, and would have made it too hard to deal with. If I wanted to stay pure, I would have had to remove the default action replay from the game, but I just liked it too much.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;level-design&quot;&gt;Level design&lt;&#x2F;h3&gt;
&lt;p&gt;Many times while I was writing Rightwaves, I wished I had written a level editor. In fact, I am often quoted as saying&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;A game without a level editor is only half a game.&quot;&lt;br &#x2F;&gt;
-- Andy Balaam, quite often&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;But instead, the level design is code like this:&lt;&#x2F;p&gt;
&lt;pre&gt;const LEVELS = [
    {
        scenery: [
            { x:   0, y: 80, image: &quot;machinery-20x16-01&quot;},
            { x:  20, y: 88, image: &quot;machinery-20x08-01&quot;},
&#x2F;&#x2F; ... etc.
        ],
        width: 1526,
        aliens: [
            newRedFlat(130, 20),
            newRedFlat(145, 25),
&#x2F;&#x2F; ... etc.
&lt;&#x2F;pre&gt;
&lt;p&gt;The full code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;public&#x2F;game.js#L3478&quot;&gt;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;public&#x2F;game.js#L3478&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not a lot of fun to edit, but on the other hand, every time you make a change a simple refresh in the browser lets you see what it really looks like. No build process; no waiting.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;graphics&quot;&gt;Graphics&lt;&#x2F;h3&gt;
&lt;p&gt;I drew the graphics for Rightwaves using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gimp.org&#x2F;&quot;&gt;GIMP&lt;&#x2F;a&gt;, exported the images as PNG files, and converted them to text using a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;images-src&#x2F;convert-image&quot;&gt;little Rust utility&lt;&#x2F;a&gt; I wrote.&lt;&#x2F;p&gt;
&lt;p&gt;When I wanted to change an image, it was a nightmare, and I would recommend this approach to no-one whatsoever.&lt;&#x2F;p&gt;
&lt;p&gt;The reason why Smolpxl supports creating images in the source code is so you can hand-craft them right there, using an ASCII-art style.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the spaceship: &lt;img style=&quot;background-color: black; padding: 10px;&quot; src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;ship.png&quot; alt=&quot;An ASCII-art spaceship&quot; width=&quot;80&quot; height=&quot;50&quot; class=&quot;aligncenter size-full wp-image-3260&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and here is the code for it:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color: black; color: rgb(100, 100, 100);&quot;&gt;const IMAGES = {
    &quot;ship&quot;: {
        pixels: [
            &quot;..&lt;span style=&quot;background-color: rgb(255, 255, 255)&quot;&gt;www&lt;&#x2F;span&gt;.......&quot;,
            &quot;&lt;span style=&quot;background-color: rgb(88, 88, 88)&quot;&gt;ddddd&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255)&quot;&gt;ww&lt;&#x2F;span&gt;.&lt;span style=&quot;background-color: rgb(77, 111, 249)&quot;&gt;bbb&lt;&#x2F;span&gt;.&quot;,
            &quot;&lt;span style=&quot;background-color: rgb(141, 0, 0)&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(126, 125, 125)&quot;&gt;lll&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(88, 88, 88)&quot;&gt;ddd&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(3, 157, 157)&quot;&gt;ccc&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255)&quot;&gt;w&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(77, 111, 249)&quot;&gt;b&lt;&#x2F;span&gt;&quot;,
            &quot;&lt;span style=&quot;background-color: rgb(88, 88, 88)&quot;&gt;ddd&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(192, 192, 192)&quot;&gt;h&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(126, 125, 125)&quot;&gt;lll&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(3, 157, 157)&quot;&gt;cccc&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(77, 111, 249)&quot;&gt;b&lt;&#x2F;span&gt;&quot;,
            &quot;&lt;span style=&quot;background-color: rgb(141, 0, 0)&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(88, 88, 88)&quot;&gt;dddddd&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(42, 42, 42)&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(3, 157, 157)&quot;&gt;cc&lt;&#x2F;span&gt;&lt;span style=&quot;background-color: rgb(77, 111, 249)&quot;&gt;b&lt;&#x2F;span&gt;.&quot;,
            &quot;..&lt;span style=&quot;background-color: rgb(42, 42, 42)&quot;&gt;aaa&lt;&#x2F;span&gt;.......&quot;
        ],
        key: {
            &quot;w&quot;: [255, 255, 255],
            &quot;d&quot;: [88, 88, 88],
            &quot;b&quot;: [77, 111, 249],
            &quot;r&quot;: [141, 0, 0],
            &quot;l&quot;: [126, 125, 125],
            &quot;h&quot;: [192, 192, 192],
            &quot;c&quot;: [3, 157, 157],
            &quot;a&quot;: [42, 42, 42]
        }
    },
&#x2F;&#x2F; ... lots, and I do mean lots, more here for the other graphics ...
&lt;&#x2F;pre&gt;
&lt;p&gt;The full code is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;public&#x2F;game.js#L41&quot;&gt;gitlab.com&#x2F;smolpxl&#x2F;rightwaves&#x2F;-&#x2F;blob&#x2F;main&#x2F;public&#x2F;game.js#L41&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note that I painstakingly coloured that code in for you, for this blog post. In the code there is no such luxury!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;openness&quot;&gt;Openness&lt;&#x2F;h2&gt;
&lt;p&gt;All the Smolpxl games are Free&#x2F;Open Source software, of course. By avoiding a build, anyone can see the source code, just as I wrote, it in their browser. Anyone (including me) can debug problems without any extra steps. Putting everything in one file makes it easier to find the code (but probably makes it harder to understand).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;optimisation&quot;&gt;Optimisation&lt;&#x2F;h2&gt;
&lt;p&gt;Rightwaves loads really fast. On my machine, the first time you visit the page, it is playing within 1.4 seconds, after downloading 5 files at a total of 149kB (gzipped). If we exclude the included action replay, it is much smaller.&lt;&#x2F;p&gt;
&lt;p&gt;All of this, with no build process.&lt;&#x2F;p&gt;
&lt;p&gt;Maybe it wasn&#x27;t such a bad idea after all.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;this-is-a-bad-idea&quot;&gt;This is a bad idea&lt;&#x2F;h2&gt;
&lt;p&gt;Don&#x27;t try this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;for-fun&quot;&gt;For fun&lt;&#x2F;h2&gt;
&lt;p&gt;Except, of course, if you like fun. I tried this because it was fun. I would heartily recommend trying things because they might be fun.&lt;&#x2F;p&gt;
&lt;p&gt;Also, if you like fun, try playing the games on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl games&lt;&#x2F;a&gt;, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;makeagame.html&quot;&gt;write your own&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Screenshot-at-2021-06-30-09-38-24-300x184.png&quot; alt=&quot;A puzzle game, a cross-the-road game, a tunnel game, a snake game, a Heli game, and a game-of-life thingy&quot; width=&quot;300&quot; height=&quot;184&quot; class=&quot;aligncenter size-medium wp-image-3273&quot;&gt;
</description>
      </item>
      <item>
          <title>Matrix is the only (chat) game in town</title>
          <pubDate>Thu, 27 May 2021 14:02:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/05/27/matrix-is-the-only-chat-game-in-town/</link>
          <guid>https://artificialworlds.net/blog/2021/05/27/matrix-is-the-only-chat-game-in-town/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/05/27/matrix-is-the-only-chat-game-in-town/">&lt;p&gt;On my phone and computer I use WhatsApp, Signal, Slack, Keybase, Discord, IRC, XMPP&#x2F;Jabber and Element&#x2F;Matrix. In addition, I occasionally use the messaging features of Mastodon, Twitter and even LinkedIn. I&#x27;ve never used Telegram, Line, WeChat, Session, Wire or Status chat, but they exist too, along with many others.&lt;&#x2F;p&gt;
&lt;p&gt;It would be better if I could chat with people using the app I prefer, rather than the one I am forced to use.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the only useful chat app is the one your friends and family are on, so it&#x27;s pointless to debate the finer points in each service&#x27;s favour, but here I go anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Only Matrix is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#encryption&quot;&gt;End-to-end encrypted&lt;&#x2F;a&gt; so people can&#x27;t eavesdrop on my conversations&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#what-does-federated-mean%3F&quot;&gt;Decentralised (federated)&lt;&#x2F;a&gt; so my identity and trust are not invested in a single company&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#why-have-you-released-this-as-open-source%3F&quot;&gt;Free&#x2F;Open Source software&lt;&#x2F;a&gt; so its security can be audited, and everyone is free to use it&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;docs&#x2F;spec&#x2F;&quot;&gt;Built on an open standard&lt;&#x2F;a&gt; so lots of systems can implement it and use it&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;foundation&#x2F;&quot;&gt;Run as a public interest foundation&lt;&#x2F;a&gt; so if supporting companies fail the project can continue&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#how-is-matrixorg-funded&quot;&gt;Developed with sustainable funding&lt;&#x2F;a&gt; so it&#x27;s less likely to disappear&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#can-i-use-matrix-without-installing-a-matrix-client%3F&quot;&gt;Able to bridge to other networks&lt;&#x2F;a&gt; so (in theory) people can keep using their existing apps&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&#x2F;faq&#x2F;#what-is-the-current-project-status&quot;&gt;Popular&lt;&#x2F;a&gt;, with about 5 million messages per day, so there is some chance my friends or family might be on it&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The importance of decentralisation has been re-emphasised for me this week after the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.sdf.org&#x2F;@kline&#x2F;106299403921451814&quot;&gt;freenode IRC debacle&lt;&#x2F;a&gt;. A single controlling entity, even when it is currently benign (as some people believe Signal is) is not a guarantee that things will stay this way. Thank goodness you can connect your usual IRC network to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;libera.chat&quot;&gt;libera.chat&lt;&#x2F;a&gt;: imagine what would happen to Signal users if they realised someone unscrupulous had acquired control.&lt;&#x2F;p&gt;
&lt;p&gt;Matrix does not solve all our problems. Notably:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Its security is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;serpentsec.1337.cx&#x2F;matrix&quot;&gt;probably not good enough&lt;&#x2F;a&gt; for people threatened by powerful interests - at the moment it&#x27;s quite easy to see who&#x27;s talking to whom, and when.&lt;&#x2F;li&gt;
&lt;li&gt;Not all clients support end-to-end encryption, and not all turn it on by default (but the most-used ones do).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Despite these limitations, Matrix is the only chat network that is even attempting to provide what users need, and it seems to be doing a pretty good job of it.&lt;&#x2F;p&gt;
&lt;p&gt;I think we should work together to address its weaknesses, and adopt it wherever we can.&lt;&#x2F;p&gt;
&lt;p&gt;So, I recommend Matrix (specifically &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;element.io&quot;&gt;element.io&lt;&#x2F;a&gt;) for group and individual chat.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Writing Snake in Terraform Video</title>
          <pubDate>Mon, 24 May 2021 12:54:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/05/24/writing-snake-in-terraform-video/</link>
          <guid>https://artificialworlds.net/blog/2021/05/24/writing-snake-in-terraform-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/05/24/writing-snake-in-terraform-video/">&lt;p&gt;This was quite a challenge, and I nearly gave up, but I managed to write a Snake game in Terraform:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Writing Snake in Terraform&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c2209f42-e0ae-40d1-838e-a4f0b2123a0b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;snake-terraform&#x2F;&quot;&gt;artificialworlds.net&#x2F;presentations&#x2F;snake-terraform&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Source code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;snake&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;snake&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Slides source code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;videos-snake&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;videos-snake&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Suspending the computer using Kupfer</title>
          <pubDate>Wed, 05 May 2021 13:53:51 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/05/05/suspending-the-computer-using-kupfer/</link>
          <guid>https://artificialworlds.net/blog/2021/05/05/suspending-the-computer-using-kupfer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/05/05/suspending-the-computer-using-kupfer/">&lt;p&gt;I have recently started using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kupferlauncher.github.io&quot;&gt;Kupfer&lt;&#x2F;a&gt; again as my application launcher in Ubuntu MATE, and I found it lacked the ability to suspend the computer.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the plugin I wrote to support this.&lt;&#x2F;p&gt;
&lt;p&gt;To install it, quit Kupfer, create a directory in your home dir called .local&#x2F;share&#x2F;kupfer&#x2F;plugins, and create this file suspend.py inside:&lt;&#x2F;p&gt;
&lt;pre&gt;__kupfer_name__ = _(&quot;Power management&quot;)
__kupfer_sources__ = (&quot;PowerManagementItemsSource&quot;, )
__description__ = _(&quot;Actions to suspend the computer&quot;)
__version__ = &quot;2021-05-05&quot;
__author__ = &quot;Andy Balaam &lt;andybalaam@artificialworlds.net&gt;&quot;

from kupfer.plugin import session_support as support

class Suspend (support.CommandLeaf):
    def __init__(self, commands):
        support.CommandLeaf.__init__(self, commands, &quot;Suspend&quot;)
    def get_description(self):
        return _(&quot;Suspend the computer&quot;)
    def get_icon_name(self):
        return &quot;system-suspend&quot;

class PowerManagementItemsSource (support.CommonSource):
	def __init__(self):
		support.CommonSource.__init__(self, _(&quot;Power management&quot;))
	def get_items(self):
		return (Suspend(([&quot;systemctl&quot;, &quot;suspend&quot;],)),)

# Copyright 2021 Andy Balaam, released under the MIT license.&lt;&#x2F;andybalaam@artificialworlds.net&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now restart Kupfer, go to Preferences, Plugins, and tick &quot;Power management&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;You should now see a &quot;Suspend&quot; item if you search for it in the Kupfer interface.&lt;&#x2F;p&gt;
&lt;p&gt;Inspired by: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ericvoid&#x2F;mate_session_kupfer_plugin&quot;&gt;Mate Session Management - Kupfer Plugin&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Reference docs: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kupferlauncher.github.io&#x2F;Documentation&#x2F;PluginAPI.html&quot;&gt;Kupfer Plugin API&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Uploading to PeerTube from the command line</title>
          <pubDate>Fri, 30 Apr 2021 09:04:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/04/30/uploading-to-peertube-from-the-command-line/</link>
          <guid>https://artificialworlds.net/blog/2021/04/30/uploading-to-peertube-from-the-command-line/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/04/30/uploading-to-peertube-from-the-command-line/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.joinpeertube.org&#x2F;api-rest-reference.html#tag&#x2F;Video&#x2F;paths&#x2F;~1videos~1upload&#x2F;post&quot;&gt;PeerTube&#x27;s API documentation&lt;&#x2F;a&gt; gives an example of how to upload a video, but it is missing a couple of important aspects, most notably how to provide multiple tags use form-encoded input, so my more complete script is below. Use it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;# First, make sure jq is installed
echo &quot;myusername&quot; &amp;gt; username
echo &quot;mypassword&quot; &amp;gt; password
.&#x2F;upload &quot;video_file.mp4&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;Downsides:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Your username and password are visible via ps to users on the same machine (tips to avoid this are welcome)&lt;&#x2F;li&gt;
&lt;li&gt;I can&#x27;t work out how to include newlines in the video description (again, tips welcome)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;You will need to edit the script to provide your own PeerTube server URL, channel ID (a number), video description, tags etc. Output and errors from the script will be placed in curl-out.txt. Read the API docs to see what numbers you need to use for category, license etc.&lt;&#x2F;p&gt;
&lt;p&gt;Here is upload:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -e
set -u

USERNAME=&quot;$(cat username)&quot;
PASSWORD=&quot;$(cat password)&quot;
FILE_PATH=&quot;$1&quot;
CHANNEL_ID=&lt;span style=&quot;color: red;&quot;&gt;MY_CHANNEL_ID_EG_1234&lt;&#x2F;span&gt;
NAME=&quot;${FILE_PATH%.*}&quot;
NAME=&quot;${NAME#*&#x2F;}&quot;

API_PATH=&quot;https:&#x2F;&#x2F;&lt;span style=&quot;color: red;&quot;&gt;MY_PEERTUBE_SERVER_URL&lt;&#x2F;span&gt;&#x2F;api&#x2F;v1&quot;
## AUTH
client_id=$(curl -s &quot;${API_PATH}&#x2F;oauth-clients&#x2F;local&quot; | jq -r &quot;.client_id&quot;)
client_secret=$(curl -s &quot;${API_PATH}&#x2F;oauth-clients&#x2F;local&quot; | jq -r &quot;.client_secret&quot;)
token=$(curl -s &quot;${API_PATH}&#x2F;users&#x2F;token&quot; \
  --data client_id=&quot;${client_id}&quot; \
  --data client_secret=&quot;${client_secret}&quot; \
  --data grant_type=password \
  --data response_type=code \
  --data username=&quot;${USERNAME}&quot; \
  --data password=&quot;${PASSWORD}&quot; \
  | jq -r &quot;.access_token&quot;)

echo &quot;Uploading ${FILE_PATH}&quot;
curl &quot;${API_PATH}&#x2F;videos&#x2F;upload&quot; \
  -H &quot;Authorization: Bearer ${token}&quot; \
  --output curl-out.txt \
  --max-time 6000 \
  --form videofile=@&quot;${FILE_PATH}&quot; \
  --form channelId=${CHANNEL_ID} \
  --form name=&quot;$NAME&quot; \
  --form category=&lt;span style=&quot;color: red;&quot;&gt;15&lt;&#x2F;span&gt; \
  --form licence=&lt;span style=&quot;color: red;&quot;&gt;7&lt;&#x2F;span&gt; \
  --form description=&quot;&lt;span style=&quot;color: red;&quot;&gt;MY_VIDEO_DESCRIPTION&lt;&#x2F;span&gt;&quot; \
  --form language=&lt;span style=&quot;color: red;&quot;&gt;en&lt;&#x2F;span&gt; \
  --form privacy=&lt;span style=&quot;color: red;&quot;&gt;1&lt;&#x2F;span&gt; \
  --form tags=&quot;&lt;span style=&quot;color: red;&quot;&gt;TAG1&lt;&#x2F;span&gt;&quot; \
  --form tags=&quot;&lt;span style=&quot;color: red;&quot;&gt;TAG2&lt;&#x2F;span&gt;&quot; \
  --form tags=&quot;&lt;span style=&quot;color: red;&quot;&gt;TAG3&lt;&#x2F;span&gt;&quot; \
  --form tags=&quot;&lt;span style=&quot;color: red;&quot;&gt;TAG4&lt;&#x2F;span&gt;&quot;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Republishing Bartosz Milewski&#x27;s Category Theory lectures</title>
          <pubDate>Wed, 21 Apr 2021 07:51:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/04/21/republishing-bartosz-milewskis-category-theory-lectures/</link>
          <guid>https://artificialworlds.net/blog/2021/04/21/republishing-bartosz-milewskis-category-theory-lectures/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/04/21/republishing-bartosz-milewskis-category-theory-lectures/">&lt;p&gt;Category Theory is an incredibly exciting and challenging area of Maths, that (among other things) can really help us understand what programming is on a fundamental level, and make us better programmers.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Category Theory&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;video-playlists&#x2F;embed&#x2F;d34fec14-28b0-4e34-bd2d-38de18e32e05&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;By far the best explanation of Category Theory that I have ever seen is a series of videos by Bartosz Milewski.&lt;&#x2F;p&gt;
&lt;p&gt;The videos on YouTube have quite a bit of background noise, and they were not available on PeerTube, so I asked for permission to edit and repost them, and Bartosz generously agreed! The conversation was in the comments section of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=I8LbkfSSR58&quot;&gt;Category Theory 1.1: Motivation and Philosophy&lt;&#x2F;a&gt; and I reproduce it below.&lt;&#x2F;p&gt;
&lt;p&gt;So, I present these awesome videos, with background noise removed using Audacity, for your enjoyment:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;w&#x2F;p&#x2F;s6rj1Dzb7rzZDsxYf7ZvpT&quot;&gt;Category Theory by Bartosz Milewski&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Permission details:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;permission.png&quot; alt=&quot;Andy Balaam: Utterly brilliant lecture series.  Is it available under a free license?  I&#x27;d like to try and clean up audio and repost it to PeerTube, if that is permitted. Bartosz Milewski: You have my permission. I consider my lectures public domain.&quot; width=&quot;1022&quot; height=&quot;466&quot; class=&quot;aligncenter size-full wp-image-3212&quot;&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Andy Balaam:&lt;&#x2F;strong&gt; Utterly brilliant lecture series. Is it available under a free license? I&#x27;d like to try and clean up audio and repost it to PeerTube, if that is permitted.&lt;br &#x2F;&gt;
&lt;strong&gt;Bartosz Milewski:&lt;&#x2F;strong&gt; You have my permission. I consider my lectures public domain.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
      <item>
          <title>Announcing I-DUNNO 1.0 and web-i-dunno</title>
          <pubDate>Thu, 01 Apr 2021 02:00:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/04/01/announcing-i-dunno-1-0-and-web-i-dunno/</link>
          <guid>https://artificialworlds.net/blog/2021/04/01/announcing-i-dunno-1-0-and-web-i-dunno/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/04/01/announcing-i-dunno-1-0-and-web-i-dunno/">&lt;p&gt;It&#x27;s hard to believe it&#x27;s already a year since the release of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc8771.html&quot;&gt;RFC 8771&lt;&#x2F;a&gt; (The Internationalized Deliberately Unreadable Network NOtation), which for me at least made me think about IP addresses in a whole new way.&lt;&#x2F;p&gt;
&lt;p&gt;So, it seems fitting for the anniversary to be able to release proper support for this standard in the Rust universe, with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;rust-i-dunno&quot;&gt;Rust I-DUNNO&lt;&#x2F;a&gt; version 1.0 released. You can find it on Rust&#x27;s crates.io at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;i-dunno&quot;&gt;crates.io&#x2F;crates&#x2F;i-dunno&lt;&#x2F;a&gt; and the API documentation is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;i-dunno&#x2F;&quot;&gt;docs.rs&#x2F;i-dunno&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Also, because for a standard like this to receive the wide adoption it deserves, it&#x27;s important that young people have a chance to interact with it, playing with encodings to get a real feel for what it&#x27;s like to use in practice, I&#x27;m proud to announce the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;andybalaam.gitlab.io&#x2F;web-i-dunno&#x2F;&quot;&gt;I-DUNNO Creator&lt;&#x2F;a&gt;. On that page you can enter an IP address (IPv4 or IPv6) and see it transformed immediately into a candidate I-DUNNO, with live information about the Confusion Level of the I-DUNNO, as specified in the standard. You can find the source code for the I-DUNNO Creator in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;web-i-dunno&quot;&gt;web-i-dunno&lt;&#x2F;a&gt; repo.&lt;&#x2F;p&gt;
&lt;p&gt;The I-DUNNO Creator is built on the Rust package, making use of Rust&#x27;s highly-developed &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rustwasm.github.io&#x2F;book&#x2F;&quot;&gt;WASM support&lt;&#x2F;a&gt; to compile the code into a form that works naturally in a web browser.&lt;&#x2F;p&gt;
&lt;p&gt;I hope that by offering both systems programmers and the young people of today and their new-fangled web sites the opportunity to create I-DUNNOs, I can contribute a little to spreading the word about deliberately unreadable notations to new audiences.&lt;&#x2F;p&gt;
&lt;p&gt;Note: the current implementation is limited to generate only I-DUNNOs with no padding bits. As specified in the standard, I-DUNNOs may end with arbitrary padding, and adding this functionality to rust-i-dunno is left as an exercise for the reader: merge requests welcome!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Automatically filling in the UK COVID test results page with Selenium IDE</title>
          <pubDate>Wed, 31 Mar 2021 20:59:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/03/31/automatically-filling-in-the-uk-covid-test-results-page-with-selenium-ide/</link>
          <guid>https://artificialworlds.net/blog/2021/03/31/automatically-filling-in-the-uk-covid-test-results-page-with-selenium-ide/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/03/31/automatically-filling-in-the-uk-covid-test-results-page-with-selenium-ide/">&lt;p&gt;Lots of people are filling in the extremely detailed UK government COVID test result page twice every week.&lt;&#x2F;p&gt;
&lt;p&gt;It asks you to fill in a very large list of details, most of which are the same every time, but it doesn&#x27;t remember what you typed last time.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t want to write a Python script or similar to enter my results, because I wanted to check I&#x27;d done it right, and because there is a captcha at the end that is clearly intended to prevent automation like that.&lt;&#x2F;p&gt;
&lt;p&gt;However, with a Selenium IDE script, I can drive my browser, watching what it does and checking the input, and manually filling in the captcha and final double-check page.&lt;&#x2F;p&gt;
&lt;p&gt;In case it&#x27;s helpful, here is the script I created: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;report-covid-test.side&quot;&gt;report-covid-test.side&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can create one for each child if you have several, filling in school name, NHS number, names, date of birth etc. in the script and re-using it, modifying it each time to enter the bar code number for the test itself.&lt;&#x2F;p&gt;
&lt;p&gt;To use it you&#x27;ll need the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;addons.mozilla.org&#x2F;en-GB&#x2F;firefox&#x2F;addon&#x2F;selenium-ide&#x2F;&quot;&gt;Selenium IDE plugin for firefox&lt;&#x2F;a&gt;, or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.selenium.dev&#x2F;selenium-ide&#x2F;&quot;&gt;Selenium IDE plugin for another browser&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d recommend loading this script into the Selenium IDE plugin in Firefox, looking through it and editing the values that say &quot;ENTER...HERE&quot;, then clicking Run Script and watching it fill in values.&lt;&#x2F;p&gt;
&lt;p&gt;It doesn&#x27;t actually submit the result, so you can always check and modify it manually if something doesn&#x27;t work out, before clicking the last couple of buttons to submit.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Toggle window decorations on Linux GTK3 with Python3</title>
          <pubDate>Tue, 23 Mar 2021 13:47:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/03/23/toggle-window-decorations-on-linux-gtk3-with-python3/</link>
          <guid>https://artificialworlds.net/blog/2021/03/23/toggle-window-decorations-on-linux-gtk3-with-python3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/03/23/toggle-window-decorations-on-linux-gtk3-with-python3/">&lt;p&gt;The Internet is full of outdated Python code for doing things with windows, so here is what I got working today in a Python 3, GTK 3 environment.&lt;&#x2F;p&gt;
&lt;p&gt;This script toggles the window decorations on the active window on and off. I have it bound to Ctrl+NumPadMinus for easy access.&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3

import gi
gi.require_version(&#x27;Gdk&#x27;, &#x27;3.0&#x27;)
gi.require_version(&#x27;GdkX11&#x27;, &#x27;3.0&#x27;)
gi.require_version(&#x27;Wnck&#x27;, &#x27;3.0&#x27;)
from gi.repository import Gdk
from gi.repository import GdkX11
from gi.repository import Wnck

def active_window(screen):
    for window in screen.get_windows():
       if window.is_active() == True:
            return window

def toggle_decorations(w):
    if w.get_decorations().decorations == 0:
        w.set_decorations(Gdk.WMDecoration.ALL)
    else:
        w.set_decorations(0)

screen = Wnck.Screen.get_default()
screen.force_update()
display = GdkX11.X11Display.get_default()
window = active_window(screen)
window_id = window.get_xid()

w = GdkX11.X11Window.foreign_new_for_display(display, window_id)
toggle_decorations(w)

window = None
screen = None
Wnck.shutdown()&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Questions about RFC 8771</title>
          <pubDate>Wed, 17 Mar 2021 09:30:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/03/17/questions-about-rfc-8771/</link>
          <guid>https://artificialworlds.net/blog/2021/03/17/questions-about-rfc-8771/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/03/17/questions-about-rfc-8771/">&lt;p&gt;During my work on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc8771.html&quot;&gt;RFC 8771 The Internationalized Deliberately Unreadable Network NOtation (I-DUNNO)&lt;&#x2F;a&gt; I have come across a number of questions. I am documenting them here so I can send them to the authors and try to improve my understanding of the intention.&lt;&#x2F;p&gt;
&lt;p&gt;This is an excellent RFC, and I thank the authors for their efforts in creating it.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Non-printable characters&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;In &lt;strong&gt;4.2. Satisfactory Confusion Level&lt;&#x2F;strong&gt;, the RFC states that encodings may be deemed Satisfactory if they contain &#x27;At least one non-printable character&#x27; (as well as one other condition in this section).&lt;&#x2F;p&gt;
&lt;p&gt;Both of the existing implementations that I know of (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lilyinstarlight&#x2F;i-dunno&quot;&gt;lilyinstartlight&#x27;s&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;audeoudh&#x2F;python-i-dunno&quot;&gt;audeoudh&#x27;s&lt;&#x2F;a&gt;) interpret &quot;printable&quot; to mean the same as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;stdtypes.html#str.isprintable&quot;&gt;Python&#x27;s isprintable() function&lt;&#x2F;a&gt;: that is Nonprintable characters are those characters defined in the Unicode character database as &quot;Other&quot; or &quot;Separator&quot;, excepting the ASCII space (0x20) which is considered printable.&lt;&#x2F;p&gt;
&lt;p&gt;However, the definition of this function may be rather Python-specific, since its intention appears to be related to language internals like the repr function.&lt;&#x2F;p&gt;
&lt;p&gt;It would be helpful to find out exactly what is meant by &quot;non-printable character&quot; in the RFC.&lt;&#x2F;p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Are Modifier Symbols, Symbols?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Also in section &lt;strong&gt;4.2&lt;&#x2F;strong&gt;, the RFC mentions &#x27;&quot;A character from the &quot;Symbol&quot; category&#x27;.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lilyinstarlight&#x2F;i-dunno&quot;&gt;Python implementation&lt;&#x2F;a&gt; excludes Modifier Symbols from its definition. I believe this is incorrect, and have logged an issue on the topic: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lilyinstarlight&#x2F;i-dunno&#x2F;issues&#x2F;5&quot;&gt;Some Symbol characters not recognised as such&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It would be helpful to have clarification on this point.&lt;&#x2F;p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;What does &quot;different directionalities&quot; mean?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Unicode classifies characters into several Bidi_Classes (for example, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;util.unicode.org&#x2F;UnicodeJsps&#x2F;character.jsp?a=CED6E&amp;amp;B1=Show&quot;&gt;U+CED6E is Left_To_Right&lt;&#x2F;a&gt;). In section  &lt;strong&gt;4.3. Delightful Confusion Level&lt;&#x2F;strong&gt;, the RFC refers to &#x27;Characters from scripts with different directionalities&#x27;.&lt;&#x2F;p&gt;
&lt;p&gt;As far as I can see there are two possible interpretations of this phrase:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The encoding should contain characters from at least two different Bidi_Classes, or&lt;&#x2F;li&gt;
&lt;li&gt;The encoding should contain characters that are both left-to-right and right-to-left in direction, either weakly or strongly.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Both current implementations interpret this statement like number 1, but I suspect the intention may actually be something more like number 2.&lt;&#x2F;p&gt;
&lt;p&gt;If number 2 was meant, I think that would mean ignoring characters with Neutral directionality, and treating weakly directional characters as the same directionality as strongly directional ones.&lt;&#x2F;p&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;What is a Confusable character?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Section &lt;strong&gt;4.3&lt;&#x2F;strong&gt; mentions &#x27;Character classified as &quot;Confusables&quot;&#x27;. Both implementations interpret this quite loosely, as meaning something like &#x27;the encoding contains any character or substring which might be confused with any other character or substring&#x27;.&lt;&#x2F;p&gt;
&lt;p&gt;This means a lot of &quot;normal&quot; characters are included: all of the ASCII digits, and many of the Latin letters.&lt;&#x2F;p&gt;
&lt;p&gt;Was this the intention?&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s all my questions. It has been great fun working on this RFC.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Announcing Rust I-DUNNO</title>
          <pubDate>Wed, 17 Mar 2021 08:52:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/03/17/announcing-rust-i-dunno/</link>
          <guid>https://artificialworlds.net/blog/2021/03/17/announcing-rust-i-dunno/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/03/17/announcing-rust-i-dunno/">&lt;p&gt;At the ACCU Conference last week I learned about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rfc-editor.org&#x2F;rfc&#x2F;rfc8771.html&quot;&gt;RFC 8771 The Internationalized Deliberately Unreadable Network NOtation (I-DUNNO)&lt;&#x2F;a&gt; from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.lunch.org.uk&quot;&gt;Jim Hague&lt;&#x2F;a&gt;, and thought it would be fun to knock up a Rust implementation.&lt;&#x2F;p&gt;
&lt;p&gt;The project is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;rust-i-dunno&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;rust-i-dunno&lt;&#x2F;a&gt; and the docs are published at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;i-dunno&#x2F;l&quot;&gt;https:&#x2F;&#x2F;docs.rs&#x2F;i-dunno&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not done yet, but encoding an IP address as I-DUNNO appears to be working:&lt;&#x2F;p&gt;
&lt;pre&gt;$ i-dunno 216.58.205.46
lYÔ®

$ i-dunno 216.58.205.46 | hexdump -C
00000000  db 81 6b 1a 2e 0a                                 |..k...|
&lt;&#x2F;pre&gt;
&lt;p&gt;Decoding is still to be done.&lt;&#x2F;p&gt;
&lt;p&gt;The implementation is seriously slow at the moment, so I am looking forward to improving it.&lt;&#x2F;p&gt;
&lt;p&gt;I am hoping it is reasonably correct - I based it on the existing &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lilyinstarlight&#x2F;i-dunno&quot;&gt;Python I-DUNNO&lt;&#x2F;a&gt; implementation and in the process found several potential bugs in that, and created some merge requests to fix bugs and help with testability.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of testability, I am building up a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;rust-i-dunno&#x2F;-&#x2F;blob&#x2F;main&#x2F;tests&#x2F;examples.txt&quot;&gt;collection of test cases&lt;&#x2F;a&gt; that could be a potential resource for other implementors, and would welcome suggestions of how this could be shared between projects. The examples so far were generated using the Python implementation, and then manually corrected where I found bugs in that, so I do not have 100% confidence that they are correct.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, have a play, and send patches and feedback!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP on Yemen</title>
          <pubDate>Mon, 01 Mar 2021 09:16:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/03/01/letter-to-my-mp-on-yemen/</link>
          <guid>https://artificialworlds.net/blog/2021/03/01/letter-to-my-mp-on-yemen/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/03/01/letter-to-my-mp-on-yemen/">&lt;p&gt;Sent via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&quot;&gt;WriteToThem&lt;&#x2F;a&gt; on Monday 1 March 2021.&lt;&#x2F;p&gt;
&lt;p&gt;Dear Ben Spencer,&lt;&#x2F;p&gt;
&lt;p&gt;Your experience on the front line in medicine means you are probably much more able than I to imagine the human impact of the situation in Yemen.&lt;&#x2F;p&gt;
&lt;p&gt;On Sunday OCHA stated that 16 million people will go hungry this year, and nearly 50,000 are already living in famine-like conditions.&lt;&#x2F;p&gt;
&lt;p&gt;How can the UK government throw away its position of leadership on this matter by cutting funding at such a desperate time? I share the urgent concern expressed by Andrew Mitchell this morning.&lt;&#x2F;p&gt;
&lt;p&gt;I would be grateful if you would pass my concerns on to the FCDO.&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
&lt;p&gt;References:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.independent.co.uk&#x2F;news&#x2F;un-warns-of-mass-famine-in-yemen-ahead-of-donor-conference-yemen-un-people-people-rebels-b1808777.html&quot;&gt;https:&#x2F;&#x2F;www.independent.co.uk&#x2F;news&#x2F;un-warns-of-mass-famine-in-yemen-ahead-of-donor-conference-yemen-un-people-people-rebels-b1808777.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.france24.com&#x2F;en&#x2F;live-news&#x2F;20210228-war-turning-yemen-into-broken-state-beyond-repair-un&quot;&gt;https:&#x2F;&#x2F;www.france24.com&#x2F;en&#x2F;live-news&#x2F;20210228-war-turning-yemen-into-broken-state-beyond-repair-un&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bbc.co.uk&#x2F;sounds&#x2F;play&#x2F;m000sqqs&quot;&gt;BBC Today Programme 2021-03-01&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Making Smolpxl work on phones and tablets</title>
          <pubDate>Sun, 07 Feb 2021 03:49:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/02/07/making-smolpxl-work-on-phones-and-tablets/</link>
          <guid>https://artificialworlds.net/blog/2021/02/07/making-smolpxl-work-on-phones-and-tablets/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/02/07/making-smolpxl-work-on-phones-and-tablets/">&lt;p&gt;I&#x27;ve added the first features intended to make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl games&lt;&#x2F;a&gt; work well on touch interfaces like phones and tablets:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;spring-touch-controls.png&quot; alt=&quot;Spring game with touch controls&quot; width=&quot;329&quot; height=&quot;458&quot; class=&quot;aligncenter size-full wp-image-3172&quot;&gt;
&lt;p&gt;I&#x27;ve added a button bar at the bottom (and moved the navigation buttons to the top).&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m looking for feedback on this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does it work on your device?&lt;&#x2F;li&gt;
&lt;li&gt;Are the buttons the right size?&lt;&#x2F;li&gt;
&lt;li&gt;Do they look ok? If not, how could they look better?&lt;&#x2F;li&gt;
&lt;li&gt;For games that require arrow keys, do you need them in the normal arrow-keys layout, or is a simple row fine?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;duckmaze-touch-controls.png&quot; alt=&quot;Duckmaze game with touch controls in a single row&quot; width=&quot;496&quot; height=&quot;464&quot; class=&quot;aligncenter size-full wp-image-3173&quot;&gt;
&lt;p&gt;If you&#x27;re writing a game and you want to add buttons like this, you just need to add a single line like this:&lt;&#x2F;p&gt;
&lt;pre&gt;game.showControls([&quot;MENU&quot;, &quot;SELECT&quot;, &quot;BUTTON1&quot;, &quot;BUTTON2&quot;]);&lt;&#x2F;pre&gt;
&lt;p&gt;or this:&lt;&#x2F;p&gt;
&lt;pre&gt;game.showControls([&quot;MENU&quot;, &quot;SELECT&quot;, &quot;LEFT&quot;, &quot;DOWN&quot;, &quot;UP&quot;, &quot;RIGHT&quot;]);&lt;&#x2F;pre&gt;
&lt;p&gt;and they should appear.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Limiting the number of open sockets in a tokio-based TCP listener</title>
          <pubDate>Fri, 08 Jan 2021 16:03:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/01/08/limiting-the-number-of-open-sockets-in-a-tokio-based-tcp-listener/</link>
          <guid>https://artificialworlds.net/blog/2021/01/08/limiting-the-number-of-open-sockets-in-a-tokio-based-tcp-listener/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/01/08/limiting-the-number-of-open-sockets-in-a-tokio-based-tcp-listener/">&lt;p&gt;I learned quite a bit today about how to think about concurrency in Rust. I was trying to use a Semaphore to limit how many open sockets my TCP listener allowed, and I had real trouble making it work. It either didn&#x27;t actually work, allowing any number of clients to connect, or the compiler told me I couldn&#x27;t do what I wanted to do, because the lifetime of my Semaphore was not &#x27;static. Here&#x27;s the journey I took towards working code that I think is correct (feedback welcome).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;motivation&quot;&gt;Motivation&lt;&#x2F;h2&gt;
&lt;p&gt;In the tokio tutorial there is a short section entitled &quot;Backpressure and bounded channels&quot; (partway down the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tokio.rs&#x2F;tokio&#x2F;tutorial&#x2F;channels&quot;&gt;Channels&lt;&#x2F;a&gt; page). It contains this statement:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;...take care to ensure total amount of concurrency is bounded. For example, when writing a TCP accept loop, ensure that the total number of open sockets is bounded.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Obviously, when I started work on a TCP accept loop, I wanted to follow this advice.&lt;&#x2F;p&gt;
&lt;p&gt;Like many things in my journey with Rust, it was harder than I expected, and eventually enlightening.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-code&quot;&gt;The code&lt;&#x2F;h2&gt;
&lt;p&gt;Here is a short Rust program that listens on a TCP port and accepts incoming connections.&lt;&#x2F;p&gt;
&lt;p&gt;Cargo.toml:&lt;&#x2F;p&gt;
&lt;pre&gt;[package]
name = &quot;tcp-listener-example&quot;
version = &quot;1.0.0&quot;
edition = &quot;2018&quot;
include = [&quot;src&#x2F;&quot;]

[dependencies]
tokio = { version = &quot;&amp;gt;=1.0.1&quot;, features = [&quot;full&quot;] }&lt;&#x2F;pre&gt;
&lt;p&gt;src&#x2F;main.rs:&lt;&#x2F;p&gt;
&lt;pre&gt;use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let listener = &lt;span style=&quot;color: green;&quot;&gt;TcpListener::bind(&quot;0.0.0.0:8080&quot;)&lt;&#x2F;span&gt;.await.unwrap();

    loop {
        let (mut tcp_stream, _) = &lt;span style=&quot;color: green;&quot;&gt;listener.accept()&lt;&#x2F;span&gt;.await.unwrap();
        &lt;span style=&quot;color: green;&quot;&gt;tokio::spawn&lt;&#x2F;span&gt;(async move {
            let mut buf: [u8; 1024] = [0; 1024];
            loop {
                let n = tcp_stream.read(&amp;amp;mut buf).await.unwrap();
                if n == 0 {
                    return;
                }
                print!(&quot;{}&quot;, String::from_utf8_lossy(&amp;amp;buf[0..n]));
            }
        });
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This program listens on port 8080, and every time a client connects, it spawns an asynchronous task to deal with it.&lt;&#x2F;p&gt;
&lt;p&gt;If I run it with:&lt;&#x2F;p&gt;
&lt;pre&gt;cargo run&lt;&#x2F;pre&gt;
&lt;p&gt;It starts, and I can connect to it from multiple other processes like this:&lt;&#x2F;p&gt;
&lt;pre&gt;telnet 0.0.0.0 8080&lt;&#x2F;pre&gt;
&lt;p&gt;Anything I type into the telnet terminal window gets printed out in the terminal where I ran cargo run. The program works: it listens on TCP port 8080 and prints out all the messages it receives.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;so-what-s-the-problem&quot;&gt;So what&#x27;s the problem?&lt;&#x2F;h3&gt;
&lt;p&gt;The problem is that this program can be overwhelmed: if lots of processes connect to it, it will accept all the connections, and eventually run out of sockets. This might prevent other things working right on the computer, or it might crash our program, or something else. We need some kind of sensible limit, as the tokio tutorial mentions.&lt;&#x2F;p&gt;
&lt;p&gt;So how do we limit the number of people allowed to connect at the same time?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;just-use-a-semaphore-dummy&quot;&gt;Just use a semaphore, dummy&lt;&#x2F;h2&gt;
&lt;p&gt;A &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Semaphore_(programming)&quot;&gt;semaphore&lt;&#x2F;a&gt; does exactly what we need here - it keeps a count of how many people are doing something, and prevents that number getting too big. So all we need to do is restrict the number of clients that we allow to connect using a semaphore.&lt;&#x2F;p&gt;
&lt;p&gt;Here was my first attempt:&lt;&#x2F;p&gt;
&lt;pre&gt;use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;
&lt;span style=&quot;color: red;&quot;&gt;use tokio::sync::Semaphore;&lt;&#x2F;span&gt;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind(&quot;0.0.0.0:8080&quot;).await.unwrap();
    &lt;span style=&quot;color: red;&quot;&gt;let sem = Semaphore::new(2);&lt;&#x2F;span&gt;

    loop {
        let (mut tcp_stream, _) = listener.accept().await.unwrap();
        &lt;span style=&quot;color: red;&quot;&gt;&#x2F;&#x2F; Don&#x27;t copy this code: it doesn&#x27;t work
        let aq = sem.try_acquire();
        if let Ok(_guard) = aq {&lt;&#x2F;span&gt;
            tokio::spawn(async move {
                let mut buf: [u8; 1024] = [0; 1024];
                loop {
                    let n = tcp_stream.read(&amp;amp;mut buf).await.unwrap();
                    if n == 0 {
                        return;
                    }
                    print!(&quot;{}&quot;, String::from_utf8_lossy(&amp;amp;buf[0..n]));
                }
            });
        &lt;span style=&quot;color: red;&quot;&gt;} else {
            println!(&quot;Rejecting client: too many open sockets&quot;);
        }&lt;&#x2F;span&gt;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This compiles fine, but it doesn&#x27;t do anything! Even though we called Semaphore::new with an argument of 2, intending to allow only 2 clients to connect, in fact I can still connect more times than that. It looks like our code changes had no effect at all.&lt;&#x2F;p&gt;
&lt;p&gt;What we were hoping to happen was that every time a client connected, we created _guard, which is a SemaphoreGuard, that occupies one of the slots in the semaphore. We were expecting that guard to live until the client disconnects, at which point the slot will be released.&lt;&#x2F;p&gt;
&lt;p&gt;Why doesn&#x27;t it work? It&#x27;s easy to understand when you think about what tokio::spawn does. It creates a task and asks for it to be executed in the future, but it doesn&#x27;t actually run it. So tokio::spawn returns immediately, and _guard is dropped, before the code that handles the request is executed. So, obviously, our change doesn&#x27;t actually restrict how many requests are being handled because the semaphore slot is freed up before the request is processed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;just-hold-the-guard-for-longer-dummy&quot;&gt;Just hold the guard for longer, dummy&lt;&#x2F;h2&gt;
&lt;p&gt;So, let&#x27;s hold on to the SemaphoreGuard for longer:&lt;&#x2F;p&gt;
&lt;pre&gt;use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;
&lt;span style=&quot;color: red;&quot;&gt;use tokio::sync::Semaphore;&lt;&#x2F;span&gt;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind(&quot;0.0.0.0:8080&quot;).await.unwrap();
    &lt;span style=&quot;color: red;&quot;&gt;let sem = Semaphore::new(2);&lt;&#x2F;span&gt;

    loop {
        let (mut tcp_stream, _) = listener.accept().await.unwrap();
        &lt;span style=&quot;color: red;&quot;&gt;let aq = sem.try_acquire();
        if let Ok(guard) = aq {&lt;&#x2F;span&gt;
            tokio::spawn(async move {
                let mut buf: [u8; 1024] = [0; 1024];
                loop {
                    let n = tcp_stream.read(&amp;amp;mut buf).await.unwrap();
                    if n == 0 {
                        &lt;span style=&quot;color: red;&quot;&gt;drop(guard);&lt;&#x2F;span&gt;
                        return;
                    }
                    print!(&quot;{}&quot;, String::from_utf8_lossy(&amp;amp;buf[0..n]));
                }
            });
        &lt;span style=&quot;color: red;&quot;&gt;} else {
            println!(&quot;Rejecting client: too many open sockets&quot;);
        }&lt;&#x2F;span&gt;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;The idea is to pass the SemaphoreGuard object into the code that actually deals with the client request. The way I&#x27;ve attempted that is by referring to guard somewhere within the async move closure. What I&#x27;ve actually done is tell it to drop guard when we are finished with the request, but actually any mention of that variable within the closure would have been enough to tell the compiler we want to move it in, and only drop it when we are done.&lt;&#x2F;p&gt;
&lt;p&gt;It all sounds reasonable, but actually this code doesn&#x27;t compile. Here&#x27;s the error I get:&lt;&#x2F;p&gt;
&lt;pre&gt;error[E0597]: `sem` does not live long enough
  --&amp;gt; src&#x2F;main.rs:12:18
   |
12 |         let aq = sem.try_acquire();
   |                  &lt;span style=&quot;color: red;&quot;&gt;^^^&lt;&#x2F;span&gt;--------------
   |                  |
   |                  &lt;span style=&quot;color: red;&quot;&gt;borrowed value does not live long enough&lt;&#x2F;span&gt;
   |                  argument requires that `sem` is borrowed for `&#x27;static`
...
29 | }
   | - `sem` dropped here while still borrowed&lt;&#x2F;pre&gt;
&lt;p&gt;What the compiler is saying is that our SemaphoreGuard is referring to sem (the Semaphore object), but that the guard might live longer than the semaphore.&lt;&#x2F;p&gt;
&lt;p&gt;Why? Surely sem is held within a scope that includes the whole of the client-handling code, so it should live long enough?&lt;&#x2F;p&gt;
&lt;p&gt;No. Actually, the async move closure that we are passing to tokio::spawn is being added to a list of tasks to run in the future, so it could live much longer. The fact that we are inside an infinite loop confused me further here, but the principle still remains: whenever we make a closure like this and pass something into it, the closure must own it, or if we are borrowing it, it must live forever (which is what a &#x27;static lifetime means).&lt;&#x2F;p&gt;
&lt;p&gt;The code above passes ownership of guard to the closure, but guard itself is referring to (borrowing) sem. This is why the compiler says that &quot;sem is borrowed for &#x27;static&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrong-things-i-tried&quot;&gt;Wrong things I tried&lt;&#x2F;h2&gt;
&lt;p&gt;Because I didn&#x27;t understand what I was doing, I tried various other things like making sem an Arc, making guard an Arc, creating guard inside the closure, and even trying to make sem actually have &#x27;static storage by making it a constant. (That last one didn&#x27;t work because only very simple types like numbers and strings can be constants.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solution-share-the-semaphore-in-an-arc&quot;&gt;Solution: Share the Semaphore in an Arc&lt;&#x2F;h2&gt;
&lt;p&gt;After what felt like too much thrashing around, I found what I think is the right answer:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;use std::sync::Arc;&lt;&#x2F;span&gt;
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;
&lt;span style=&quot;color: green;&quot;&gt;use tokio::sync::Semaphore;&lt;&#x2F;span&gt;

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind(&quot;0.0.0.0:8080&quot;).await.unwrap();
    &lt;span style=&quot;color: green;&quot;&gt;let sem = Arc::new(Semaphore::new(2));&lt;&#x2F;span&gt;

    loop {
        let (mut tcp_stream, _) = listener.accept().await.unwrap();
        &lt;span style=&quot;color: green;&quot;&gt;let sem_clone = Arc::clone(&amp;amp;sem);&lt;&#x2F;span&gt;
        tokio::spawn(async move {
            &lt;span style=&quot;color: green;&quot;&gt;let aq = sem_clone.try_acquire();
            if let Ok(_guard) = aq {&lt;&#x2F;span&gt;
                let mut buf: [u8; 1024] = [0; 1024];
                loop {
                    let n = tcp_stream.read(&amp;amp;mut buf).await.unwrap();
                    if n == 0 {
                        return;
                    }
                    print!(&quot;{}&quot;, String::from_utf8_lossy(&amp;amp;buf[0..n]));
                }
            &lt;span style=&quot;color: green;&quot;&gt;} else {
                println!(&quot;Rejecting client: too many open sockets&quot;);
            }&lt;&#x2F;span&gt;
        });
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This code:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Creates a Semaphore and stores it inside an Arc, which is a reference-counting pointer that can be shared between tasks. This means it will live as long as someone holds a reference to it.&lt;&#x2F;li&gt;
&lt;li&gt;Clones the Arc so we have a copy that can be safely moved into the async move closure. We can&#x27;t move sem in to the closure because it&#x27;s going to get used again the next time around the loop. We can move sem_clone in to the closure because it&#x27;s not used anywhere else. sem and sem_clone both refer to the same Semaphore object, so they agree on the count of clients that are connected, but they are different Arc instances, so one can be moved into the closure.&lt;&#x2F;li&gt;
&lt;li&gt;Only aquires the SemaphoreGuard once we&#x27;re inside the closure. This way we&#x27;re not doing something difficult like borrowing a reference to something that lives outside the closure. Instead, we&#x27;re borrowing a reference via sem_clone, which is owned by the closure which we are inside, so we know it will live long enough.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It actually works! After two clients are connected, listener.accept actually opens a socket to any new client, but because we return almost immediately from the closure, we only hold it open very briefly before dropping it. This seemed preferable to refusing to open it at all, which I thought would probably leave clients hanging, waiting for a connection that might never come.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lifetimes-are-cool-and-tricky&quot;&gt;Lifetimes are cool, and tricky&lt;&#x2F;h2&gt;
&lt;p&gt;Once again, I have learned a lot about what my code is really doing from the Rust compiler. I find this stuff really confusing, but hopefully by writing down my understanding in this post I have helped my current and future selves, and maybe even you, be clearer about how to share a semaphore between multiple asynchronous tasks.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s really fun and empowering to write code that I am reasonably confident is correct, and also works. The sense that &quot;the compiler has my back&quot; is strong, and I like it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Recommendation against the use of WhatsApp in your company</title>
          <pubDate>Fri, 08 Jan 2021 13:41:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/01/08/recommendation-against-the-use-of-whatsapp-in-your-company/</link>
          <guid>https://artificialworlds.net/blog/2021/01/08/recommendation-against-the-use-of-whatsapp-in-your-company/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/01/08/recommendation-against-the-use-of-whatsapp-in-your-company/">&lt;p&gt;Here is the email I just sent to the organisation I volunteer for. Feel free to adapt and use in your context.&lt;&#x2F;p&gt;
&lt;p&gt;Dear &lt;em&gt;[organisation leaders]&lt;&#x2F;em&gt;,&lt;&#x2F;p&gt;
&lt;p&gt;Much of the tech industry (e.g. [1]) is warning against the use of WhatsApp due to its policy of collecting and sharing user information with third parties and the poor track record of its parent company (Facebook) on ethical issues (see examples [2] and [3], and many more).&lt;&#x2F;p&gt;
&lt;p&gt;The situation was made considerably worse with a recent change to the WhatsApp terms and conditions [4].&lt;&#x2F;p&gt;
&lt;p&gt;So, as your IT person I recommend not using WhatsApp for our work.&lt;&#x2F;p&gt;
&lt;p&gt;We already have an alternative available, and I would be really happy to help anyone who needs help setting it up.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;[Details here of the alternative we use (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zulip.com&#x2F;&quot;&gt;Zulip&lt;&#x2F;a&gt;) and how to use it. The simplest alternative to recommend is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;signal.org&#x2F;&quot;&gt;Signal&lt;&#x2F;a&gt;.]&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thanks, Andy&lt;&#x2F;p&gt;
&lt;p&gt;[1] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eff.org&#x2F;deeplinks&#x2F;2016&#x2F;08&#x2F;what-facebook-and-whatsapps-data-sharing-plans-really-mean-user-privacy-0&quot;&gt;What Facebook and WhatsApp&#x27;s Data Sharing Plans Really Mean for User Privacy&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[2] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theguardian.com&#x2F;technology&#x2F;2014&#x2F;jun&#x2F;29&#x2F;facebook-users-emotions-news-feeds&quot;&gt;Facebook experimented with modifying people&#x27;s moods&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[3] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theverge.com&#x2F;2019&#x2F;1&#x2F;29&#x2F;18202880&#x2F;facebook-research-enterprise-root-certificate-onavo-techcrunch&quot;&gt;Facebook paid teens for total access to their phone activity&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[4] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theregister.com&#x2F;2021&#x2F;01&#x2F;06&#x2F;whatsapp_privacy_policy_demand&#x2F;&quot;&gt;If you&#x27;re a WhatsApp user, you&#x27;ll have to share your personal data&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Streaming video with Owncast on a free Oracle Cloud computer</title>
          <pubDate>Tue, 05 Jan 2021 03:15:12 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/01/05/streaming-video-with-owncast-on-a-free-oracle-cloud-computer/</link>
          <guid>https://artificialworlds.net/blog/2021/01/05/streaming-video-with-owncast-on-a-free-oracle-cloud-computer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/01/05/streaming-video-with-owncast-on-a-free-oracle-cloud-computer/">&lt;p&gt;I just streamed about 40 minutes of me playing Trials Fusion using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;owncast.online&quot;&gt;Owncast&lt;&#x2F;a&gt;. Owncast is a self-hosted alternative to streaming services like Twitch and YouTube live.&lt;&#x2F;p&gt;
&lt;p&gt;Normally, you would need to pay for a computer to self-host it on. Owncast suggest this will cost about $5&#x2F;month.&lt;&#x2F;p&gt;
&lt;p&gt;But, Oracle Cloud has a &quot;Always Free&quot; tier that includes a &quot;Compute Instance&quot; (a virtual machine running Linux) that is capable of running Owncast.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I did it:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;register-for-oracle-cloud&quot;&gt;Register for Oracle Cloud&lt;&#x2F;h2&gt;
&lt;p&gt;This was probably the worst bit.&lt;&#x2F;p&gt;
&lt;p&gt;I went to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;oraclecloud.com&#x2F;&quot;&gt;oraclecloud.com&lt;&#x2F;a&gt; and clicked &quot;Sign up for free cloud tier&quot;. It didn&#x27;t work in Firefox(!) so I had to use Chromium.&lt;&#x2F;p&gt;
&lt;p&gt;I had to enter my name, address, email address, phone number and credit card details. The email was verified, the phone number was verified (with a text message), and the credit card was verified (with a real transaction), so there was no getting around any of it.&lt;&#x2F;p&gt;
&lt;p&gt;They promise that they won&#x27;t charge my card. I&#x27;ll let you know if I discover differently.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;create-a-compute-instance&quot;&gt;Create a Compute Instance&lt;&#x2F;h2&gt;
&lt;p&gt;Once I was logged in to the Oracle &quot;console&quot; (web site), I clicked the burger menu in the top left, chose &quot;Compute&quot; and then &quot;Instances&quot; to create a new instance. I followed all the default settings (including using the default &quot;image&quot;, which meant my instance was running Oracle Linux, which I think is similar to Red Hat), and when I got to the ssh keys part, I supplied the public key of my existing SSH key pair. Read the docs there if you don&#x27;t have one of these.&lt;&#x2F;p&gt;
&lt;p&gt;As soon as that was done, and I waited for the instance to be created and started, I was able to SSH in to my instance using a username of opc and the Public IP Address listed:&lt;&#x2F;p&gt;
&lt;pre&gt;ssh opc@PUBLIC_IP&lt;&#x2F;pre&gt;
&lt;p&gt;(Note: here and below, if I say &quot;PUBLIC_IP&quot;, I mean the IP address listed in the information about your compute instance. It should be a list of four numbers separated by dots.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;allow-connecting-to-the-instance-on-different-ports&quot;&gt;Allow connecting to the instance on different ports&lt;&#x2F;h2&gt;
&lt;p&gt;Owncast listens for HTTP connections on port 8080, and RTMP streams on 1935, so I needed to do two things to make that work.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;modify-the-security-list-to-add-ingress-rules&quot;&gt;Modify the Security List to add Ingress Rules&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;On the information about my instance, I clicked on the name of the Subnet (under Primary VNIC).&lt;&#x2F;li&gt;
&lt;li&gt;In the subnet, I clicked the name of the Security List (&quot;Default Security List for ...&quot;) in the Security Lists list.&lt;&#x2F;li&gt;
&lt;li&gt;In the Security List I clicked Add Ingress Rules and entered:&lt;pre&gt;Stateless: unchecked
Source Type: CIDR
Source CIDR: 0.0.0.0&#x2F;0
IP Protocol: TCP
Source Port Range: (blank)
Destination Port Range: 8080
Description: (blank)&lt;&#x2F;pre&gt;and then clicked Add Ingress Rules to create the rule.&lt;&#x2F;li&gt;
&lt;li&gt;I then added another Ingress Rule that was identical, except Destination Port Range was 1935.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;allow-ports-8080-and-1935-on-the-instance-s-own-firewall&quot;&gt;Allow ports 8080 and 1935 on the instance&#x27;s own firewall&lt;&#x2F;h3&gt;
&lt;p&gt;It took me a long time to figure out, but it turns out the Oracle Linux running on the Compute Instance has its own firewall. Eventually, thanks to a blog post by meinside: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.meinside.dev&#x2F;When-Oracle-Clouds-Ubuntu-Instance-Doesnt-Accept-Connections-to-Ports-Other-than-22&#x2F;&quot;&gt;When Oracle Cloud&#x27;s Ubuntu instance doesn&#x27;t accept connections to ports other than 22&lt;&#x2F;a&gt;, and some Oracle docs on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.oracle.com&#x2F;en-us&#x2F;iaas&#x2F;Content&#x2F;Network&#x2F;Concepts&#x2F;waystosecure.htm&quot;&gt;ways to secure resources&lt;&#x2F;a&gt;, I found that I needed to SSH in to the machine (like I showed above) and run these commands:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo firewall-cmd --zone=public --permanent --add-port=8080&#x2F;tcp
sudo firewall-cmd --zone=public --permanent --add-port=1935&#x2F;tcp
sudo firewall-cmd --reload&lt;&#x2F;pre&gt;
&lt;p&gt;Now I was able to connect to the services I ran on the machine on those ports.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;install-owncast&quot;&gt;Install Owncast&lt;&#x2F;h2&gt;
&lt;p&gt;The Owncast install was incredibly easy. I just followed the instructions at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;owncast.online&#x2F;docs&#x2F;quickstart&#x2F;&quot;&gt;Owncast Quickstart&lt;&#x2F;a&gt;. I SSHd in to the instance as before, and ran:&lt;&#x2F;p&gt;
&lt;pre&gt;curl -s https:&#x2F;&#x2F;owncast.online&#x2F;install.sh | bash&lt;&#x2F;pre&gt;
&lt;p&gt;and then edited the file owncast&#x2F;config.yaml to have a custom stream key in it. You can do that by typing:&lt;&#x2F;p&gt;
&lt;pre&gt;nano owncast&#x2F;config.yaml&lt;&#x2F;pre&gt;
&lt;p&gt;There is information about this file at: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;owncast.online&#x2F;docs&#x2F;configuration&#x2F;&quot;&gt;owncast.online&#x2F;docs&#x2F;configuration&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;run-owncast&quot;&gt;Run Owncast&lt;&#x2F;h2&gt;
&lt;p&gt;I ran the service like this:&lt;&#x2F;p&gt;
&lt;pre&gt;cd owncast
.&#x2F;owncast&lt;&#x2F;pre&gt;
&lt;p&gt;In future, if I want to leave it running, I may run it inside screen, or even use systemd or similar.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;open-the-web-site&quot;&gt;Open the web site&lt;&#x2F;h2&gt;
&lt;p&gt;I could now see the web site by typing this into my browser&#x27;s address bar:&lt;&#x2F;p&gt;
&lt;pre&gt;http:&#x2F;&#x2F;PUBLIC_IP:8080&lt;&#x2F;pre&gt;
&lt;p&gt;(Where PUBLIC_IP is the Public IP copied from the Instance info as before.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;stream-some-video&quot;&gt;Stream some video&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsproject.com&#x2F;&quot;&gt;OBS&lt;&#x2F;a&gt;&#x27;s Settings I chose the Stream section and entered:&lt;&#x2F;p&gt;
&lt;pre&gt;Service: Custom...
Server: rtmp:&#x2F;&#x2F;PUBLIC_IP&#x2F;live
Stream key: STREAM_KEY&lt;&#x2F;pre&gt;
&lt;p&gt;Where &quot;STREAM_KEY&quot; means the stream key I added to config.yaml earlier.&lt;&#x2F;p&gt;
&lt;p&gt;Now, when I clicked &quot;Start Streaming&quot; in OBS, my stream appeared on the web site!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;costs-and-limits&quot;&gt;Costs and limits&lt;&#x2F;h2&gt;
&lt;p&gt;Oracle stated during sign-up that I would not be charged unless I explicitly chose to use a different tier.&lt;&#x2F;p&gt;
&lt;p&gt;The Compute Instance is part of the &quot;Always Free&quot; tier, so in theory it should stay up and working.&lt;&#x2F;p&gt;
&lt;p&gt;However, if you use lots of resources (which streaming for a long time probably does), I would expect services would be throttled and&#x2F;or stopped completely. I have no idea whether they will allow enough resources for regular streaming, or whether this is all waste of time. We shall see.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Pinephone update</title>
          <pubDate>Mon, 04 Jan 2021 16:29:26 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/01/04/pinephone-update/</link>
          <guid>https://artificialworlds.net/blog/2021/01/04/pinephone-update/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/01/04/pinephone-update/">&lt;p&gt;I got a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.pine64.org&#x2F;pinephone&#x2F;&quot;&gt;Pinephone&lt;&#x2F;a&gt; for Christmas!&lt;&#x2F;p&gt;
&lt;p&gt;Here is quick summary of my experience with it. (Originally published on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;web&#x2F;statuses&#x2F;105498315777930274&quot;&gt;mastodon&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;code&gt;
Update on the pinephone as promised.
&lt;p&gt;I love it, but I would definitely not recommend expecting to use it as your actual phone.&lt;&#x2F;p&gt;
&lt;p&gt;I have the Manjaro Phosh edition.  Phosh is GNOME customised for mobile.&lt;&#x2F;p&gt;
&lt;p&gt;It turns on, you can unlock it, and you get a launcher.  It has apps, and some of them work.&lt;&#x2F;p&gt;
&lt;p&gt;Firefox works really well.  I can use it for Youtube and loads of other sites.  I installed uBlock Origin, and it works.&lt;&#x2F;p&gt;
&lt;p&gt;Adding my Nextcloud config to Phosh seamlessly gave me Calendar, Contact and TODO list apps working, with my data in them.&lt;&#x2F;p&gt;
&lt;p&gt;The Maps app found me easily via GPS.  I could bring up directions by entering a from and to, but it didn&#x27;t seem to want to guide me via GPS.&lt;&#x2F;p&gt;
&lt;p&gt;Several apps don&#x27;t fit properly on screen, and there doesn&#x27;t seem to be a way to scroll or move the windows.&lt;&#x2F;p&gt;
&lt;p&gt;The camera technically works but the picture looks terrible (squashed, wibbly and blue-coloured).&lt;&#x2F;p&gt;
&lt;p&gt;Scrolling around on the launcher updates at about 5-10 fps, which is fine but would put many people off.&lt;&#x2F;p&gt;
&lt;p&gt;Many of the apps available to install in the Software app don&#x27;t really work.  I assume the list of apps is the standard for GNOME or Manjaro, so many are not adapted for phones.&lt;&#x2F;p&gt;
&lt;p&gt;I &lt;em&gt;love&lt;&#x2F;em&gt; the fact that all the work that has been put into desktop Linux can be re-used on phones.  Why wasn&#x27;t it always this way?&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s great to be able to buy hardware that is specifically designed to run properly free software.&lt;&#x2F;p&gt;
&lt;p&gt;The Terminal app works nicely and presents a keyboard with extra keys that you need in a terminal.&lt;&#x2F;p&gt;
&lt;p&gt;The settings app works nicely.&lt;&#x2F;p&gt;
&lt;p&gt;My biggest frustration was not being able to find software in the Software app that worked nicely.&lt;&#x2F;p&gt;
&lt;p&gt;I was looking for a Youtube app that protected my privacy.  On Android I use NewPipe Legacy.  On desktop I use Freetube.  I couldn&#x27;t find Freetube in Software.  I tried Minitube but it was unusable (window didn&#x27;t fit).&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t tried installing software from the command line.  Maybe I can find (or build) Freetube via a Manjaro repo?&lt;&#x2F;p&gt;
&lt;p&gt;Or maybe I should investigate NewPipe Legacy via anbox, although that seems to miss the point a little :-)&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Is your program a function or a service?</title>
          <pubDate>Mon, 04 Jan 2021 12:29:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2021/01/04/is-your-program-a-function-or-a-service/</link>
          <guid>https://artificialworlds.net/blog/2021/01/04/is-your-program-a-function-or-a-service/</guid>
          <description xml:base="https://artificialworlds.net/blog/2021/01/04/is-your-program-a-function-or-a-service/">&lt;p&gt;Maybe everyone knows this already, but for my own clarity, I think there are really two types of computer program:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A function: something that you run, and get back a result. Example: a command-line tool like ls&lt;&#x2F;li&gt;
&lt;li&gt;A service: something that sits around waiting for things to happen, and responds to them. Example: a web server&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-functions-work&quot;&gt;How functions work&lt;&#x2F;h2&gt;
&lt;p&gt;Programs that are essentially functions should:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Validate their input and stop if it is wrong&lt;&#x2F;li&gt;
&lt;li&gt;Stop when they have finished their job*&lt;&#x2F;li&gt;
&lt;li&gt;Let you know whether they succeeded or failed&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;*The Halting Problem shows that you can&#x27;t prove they stop, so I won&#x27;t ask you to do that.&lt;&#x2F;p&gt;
&lt;p&gt;Writing functions is relatively easy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-services-work&quot;&gt;How services work&lt;&#x2F;h2&gt;
&lt;p&gt;Programs that are services should:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Start when you tell them to start, even when things are not right&lt;&#x2F;li&gt;
&lt;li&gt;Keep running until you tell them to stop, even when bad things happen&lt;&#x2F;li&gt;
&lt;li&gt;Tell the user about problems via some communication mechanism&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Writing services seems a little harder than writing functions.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-about-uis&quot;&gt;What about UIs?&lt;&#x2F;h2&gt;
&lt;p&gt;I suggest that programs with UIs are just a special case of services. Do you agree?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-about-let-it-crash&quot;&gt;What about let-it-crash?&lt;&#x2F;h2&gt;
&lt;p&gt;I think that &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stratus3d.com&#x2F;blog&#x2F;2020&#x2F;01&#x2F;20&#x2F;applying-the-let-it-crash-philosophy-outside-erlang&#x2F;&quot;&gt;let-it-crash&lt;&#x2F;a&gt; is a good way to build services, but when you build a service that way, I consider the &lt;strong&gt;whole system&lt;&#x2F;strong&gt; to be the real service: this means the code we are writing, plus the runtime. In this case, the runtime is responsible for keeping the service running (by restarting it), and telling the user about problems.&lt;&#x2F;p&gt;
&lt;p&gt;In effect, let-it-crash allows us to write programs that look like functions (which I claim is easier), and still have them behave like services, because the runtime does the extra work for us. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.erlang.org&#x2F;&quot;&gt;Erlang&lt;&#x2F;a&gt; seems like a good example of this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-the-implications&quot;&gt;What are the implications?&lt;&#x2F;h2&gt;
&lt;p&gt;If you are writing a service, your program should start when asked, and keep running until it is asked to stop, even if things are bad.&lt;&#x2F;p&gt;
&lt;p&gt;For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a service that relies on a data source should keep running when that data source is unavailable, and emit errors saying that it is unable to work. It should start working when the data source becomes available. (Again, if you implement this behaviour by using a runtime that allows you to write in a let-it-crash style, good for you.)&lt;&#x2F;li&gt;
&lt;li&gt;a service that relies on the existence of a directory should probably create that directory if it doesn&#x27;t exist.&lt;&#x2F;li&gt;
&lt;li&gt;a service that needs config might want to start up with sane defaults if the config is not supplied. Or maybe it should complain loudly and poll for the file to be created?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;why-not-stop-when-things-are-wrong&quot;&gt;Why not stop when things are wrong?&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Using this approach, it doesn&#x27;t matter the order of starting services. The more services we have, the more painful it is to have an order we must follow.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s nice when things are predictable. We expect services to keep running under normal circumstances. Using this approach, our expectations are not wrong when things go wrong.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-are-the-down-sides&quot;&gt;What are the down sides?&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;You must pay attention to the error reporting coming from running services - they may not be working.&lt;&#x2F;li&gt;
&lt;li&gt;Services will still stop, due to bugs, or at least due to hardware failures, so you still have to pay attention to whether services are running.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;More: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@kelseyhightower&#x2F;12-fractured-apps-1080c73d481c&quot;&gt;12 Fractured Apps&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Shutdown order consistency: how Rust helps</title>
          <pubDate>Tue, 22 Dec 2020 16:25:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/12/22/shutdown-order-consistency-how-rust-helps/</link>
          <guid>https://artificialworlds.net/blog/2020/12/22/shutdown-order-consistency-how-rust-helps/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/12/22/shutdown-order-consistency-how-rust-helps/">&lt;h2 id=&quot;some-java-code-with-bugs&quot;&gt;Some Java code with bugs&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s my main method (in Java). Can you guess the bug?&lt;&#x2F;p&gt;
&lt;pre&gt;Db db = new Db();
Monitoring monitoring = new Monitoring();
Monitoring mon2 = new Monitoring();
Billing billing = new Billing(db, monitoring);
monitoring.setDb(db);

runMainLoop(billing, mon2);

db.stop();
billing.stop();
monitoring.stop();&lt;&#x2F;pre&gt;
&lt;p&gt;If you would like to hunt down the 2 bugs manually, try reading the full code here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;-&#x2F;snippets&#x2F;2053222&quot;&gt;ShutdownOrder.java&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But maybe you have an idea already? Maybe you&#x27;ve seen code like this before? If you have, you probably have an instinct that there&#x27;s some kind of bug, even if you can&#x27;t say for sure what it is. Code like this almost always has bugs!&lt;&#x2F;p&gt;
&lt;p&gt;This code compiles fine, but it contains two bugs.&lt;&#x2F;p&gt;
&lt;p&gt;First, we forgot to setDb() on mon2. This causes a NullPointerException, because Monitoring expects always to have a working Db.&lt;&#x2F;p&gt;
&lt;p&gt;Second, and in general harder to spot, we shut down our services in the wrong order. It turns out that Monitoring uses its Db during shutdown, so we get an exception. Even worse, if some other code needed to run after monitoring.stop(), it won&#x27;t, because the exception prevents us getting any further.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, this is toy code, but this kind of problem is common (and much harder to spot) in real-life code. In fact, my team dealt with a similar bug this week.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s fundamentally hard to figure out your shutdown order. It&#x27;s complicated further if classes have start() methods too, which I have seen in lots of Java code.&lt;&#x2F;p&gt;
&lt;p&gt;Given that this is just a hard problem, maybe there&#x27;s no point looking for tools to make it easier?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;some-rust-code-without-those-bugs&quot;&gt;Some Rust code without those bugs&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s try writing this code in Rust. Here&#x27;s the main method:&lt;&#x2F;p&gt;
&lt;pre&gt;let db = Db::new();
let monitoring = Monitoring::new(&amp;amp;db);
let mon2 = Monitoring::new(&amp;amp;db);
let billing = Billing::new(&amp;amp;db, &amp;amp;monitoring);

run_main_loop(&amp;amp;billing, &amp;amp;mon2);

&#x2F;&#x2F; drop() is called automatically on all objects here&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s the full code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;-&#x2F;snippets&#x2F;2053243&quot;&gt;shutdown_order.rs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This code shuts down all the services automatically at the end, and any mistakes we make in the order are compile errors, not things we find later when our code is running.&lt;&#x2F;p&gt;
&lt;p&gt;The code to shut down each service looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;impl Drop for Monitoring&amp;lt;&#x27;_&amp;gt; {
    fn drop(&amp;amp;mut self) {
        &#x2F;&#x2F; [Disconnect from monitoring API]
        self.db.add_record(&quot;MonitorShutDown&quot;);
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This is us implementing the Drop trait for the struct Monitoring (traits are a bit like Java Interfaces). The Drop trait is special: it indicates what to do when an instance of this struct is dropped. In Rust, this is guaranteed to happen when the instance goes out of scope, which is why our comment at the end of the main method sounds so confident.&lt;&#x2F;p&gt;
&lt;p&gt;Furthermore, Rust&#x27;s compiler shuts down everything in the reverse order in which it was created, and guarantees that nothing gets used after it has been dropped.&lt;&#x2F;p&gt;
&lt;p&gt;Rust&#x27;s lovely world gives us two relevant treats: no unexpected nulls, and lifetimes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;treat-number-1-no-unexpected-nulls&quot;&gt;Treat number 1: no unexpected nulls&lt;&#x2F;h3&gt;
&lt;p&gt;First, in Rust, like in other modern languages like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kotlinlang.org&#x2F;&quot;&gt;Kotlin&lt;&#x2F;a&gt;, we have to be explicit about items that could be missing. In our example, we were able to re-arrange the code so that db can never be missing (or null), and the compiler encouraged us to do so. If we really needed it to be missing some of the time, we could have used the Option type, and the compiler would have forced us to handle the case when it was missing, instead of unexpectedly getting a NullPointerException like we did in Java. (In fact, if we&#x27;d structured our code to use final in as many places as possible, we could have been encouraged towards basically the same solution in Java too.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;treat-number-2-lifetimes&quot;&gt;Treat number 2: lifetimes&lt;&#x2F;h3&gt;
&lt;p&gt;Second, if you look a bit more closely at the full code of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;-&#x2F;snippets&#x2F;2053243&quot;&gt;shutdown_order.rs&lt;&#x2F;a&gt; you&#x27;ll see lots of confusing-looking annotations like &amp;lt;&#x27;a&amp;gt; and &amp;amp;&#x27;a:&lt;&#x2F;p&gt;
&lt;pre&gt;struct Monitoring&lt;span style=&quot;color:green;&quot;&gt;&amp;lt;&#x27;a&amp;gt;&lt;&#x2F;span&gt; {
    db: &lt;span style=&quot;color:green;&quot;&gt;&amp;amp;&#x27;a&lt;&#x2F;span&gt; Db,
}&lt;&#x2F;pre&gt;
&lt;p&gt;The approximate meaning of those annotations is: a Monitoring holds a reference to a Db, and that Db must last longer than the Monitoring.&lt;&#x2F;p&gt;
&lt;p&gt;This &quot;lasts longer than&quot; wording is what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch10-03-lifetime-syntax.html&quot;&gt;Rust Lifetimes&lt;&#x2F;a&gt; are for. Lifetimes are a way of saying how long something lasts.&lt;&#x2F;p&gt;
&lt;p&gt;Lifetimes are really confusing when you start with Rust, and have caused me a lot of pain. Code like this is where they are both most painful and most helpful. As I mentioned earlier, the problem of shutdown order is fundamentally hard. Rust gives you that pain at the beginning, and until you understand what&#x27;s going on, the pain is very confusing and acute. But, once your code compiles, it is correct, at least as far as problems like this are concerned.&lt;&#x2F;p&gt;
&lt;p&gt;I love the sense of security it gives me to write Rust code and know the compiler has checked my code for this kind of problem, meaning it can&#x27;t crop up at 3am on Christmas Day...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;final-note-caveat&quot;&gt;Final note&#x2F;caveat&lt;&#x2F;h2&gt;
&lt;p&gt;This Rust code is probably over-simplified, because all the references are immutable (you can&#x27;t change the objects they point to). In practice, we may well have mutable references, and if we do we&#x27;re going have to deal with the further difficulty that Rust won&#x27;t allow two different objects to hold references to an object if any of those references are mutable. So it would object to Billing and Monitoring using the Db object at the same time. We&#x27;d need to make it immutable (as we have here), or find a different way of structuring the code: for example, we could hold the Db instance only within the run_main_loop code, and pass it in temporarily to the Billing and Monitoring objects when we called their methods. A large part of the art, fun and pain of learning Rust is finding new patterns for your code that do what you need to do and also keep the compiler happy. When you manage it, you get amazing benefits!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Edge computing providers</title>
          <pubDate>Thu, 17 Dec 2020 17:24:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/12/17/edge-computing-providers/</link>
          <guid>https://artificialworlds.net/blog/2020/12/17/edge-computing-providers/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/12/17/edge-computing-providers/">&lt;p&gt;I&#x27;m looking into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Edge_computing&quot;&gt;Edge computing&lt;&#x2F;a&gt; at work. By Edge computing I mean running WASM programs in lots and lots of smallish computers in places near to actual people (rather than in huge cloud data centres). I think it&#x27;s cool because I love Rust, and Rust is the leading language to compile to WASM.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some companies providing Edge computing services:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.fastly.com&#x2F;&quot;&gt;Fastly&lt;&#x2F;a&gt; - good links with WASM community (hired Mozilla devs), and early adopters - custom WASM engine wasmtime.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;workers.cloudflare.com&#x2F;&quot;&gt;Cloudflare&lt;&#x2F;a&gt; - huge, and early adopters - WASM engine is Google V8.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;lambda&#x2F;edge&#x2F;&quot;&gt;AWS Lambda@Edge&lt;&#x2F;a&gt; - docs are light on detail, but it looks like a real offering, probably.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also-rans:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;blog&#x2F;products&#x2F;gcp&#x2F;bringing-intelligence-edge-cloud-iot&quot;&gt;couldn&#x27;t see how to get started&lt;&#x2F;a&gt; on Google cloud.&lt;&#x2F;li&gt;
&lt;li&gt;Microsoft Azure &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-gb&#x2F;azure&#x2F;networking&#x2F;edge-zones-overview&quot;&gt;appears not to be ready for general use&lt;&#x2F;a&gt; yet?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Who did I miss?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Schema upgrades should be reversible (also other transformations, actually)</title>
          <pubDate>Thu, 17 Dec 2020 15:54:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/12/17/schema-upgrades-should-be-reversible-also-other-transformations-actually/</link>
          <guid>https://artificialworlds.net/blog/2020/12/17/schema-upgrades-should-be-reversible-also-other-transformations-actually/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/12/17/schema-upgrades-should-be-reversible-also-other-transformations-actually/">&lt;p&gt;Are you writing schema upgrade code? Then I humbly suggest you take the time to write schema downgrade code too.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Why would I do that?&quot; you might well ask, &quot;I won&#x27;t ever need to downgrade.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Now, I imagine you&#x27;re expecting me to say you actually will need to downgrade, but that isn&#x27;t what I&#x27;m saying.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;can-you-please-get-on-with-what-you-are-actually-saying&quot;&gt;Can you please get on with what you are actually saying?&lt;&#x2F;h2&gt;
&lt;p&gt;Whevener you write code to transform something, be it a schema upgrade, some serialisation, or something else, I would highly recommend that you write code to transform it in both directions.&lt;&#x2F;p&gt;
&lt;p&gt;Reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It makes testing easier. The best kinds of tests for things like this are round-trips, where you transform something in both directions and check it hasn&#x27;t changed. It&#x27;s really hard to mess up tests like that.&lt;&#x2F;li&gt;
&lt;li&gt;It often uncovers bugs, because it enforces clear thinking about what the transformation actually means.&lt;&#x2F;li&gt;
&lt;li&gt;It may improve your code, because it gets annoying writing similar-but-different code to transform in both directions, so you are pushed towards some kind of abstraction.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Also:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You almost certainly are going to need it. Sometimes things go wrong and you need to back up.&lt;&#x2F;li&gt;
&lt;li&gt;It will be incredibly useful for testing other parts of your code.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Bidirectional scheme up&#x2F;downgrades are not easy in SQL, but probably worth it. If you&#x27;re writing transformation code in a normal programming language, it&#x27;s really not that difficult, and I predict it will be worth it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Make levels for Rabbit Escape using the level editor!</title>
          <pubDate>Thu, 10 Dec 2020 16:15:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/12/10/make-levels-for-rabbit-escape-using-the-level-editor/</link>
          <guid>https://artificialworlds.net/blog/2020/12/10/make-levels-for-rabbit-escape-using-the-level-editor/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/12/10/make-levels-for-rabbit-escape-using-the-level-editor/">&lt;p&gt;You can make levels for Rabbit Escape!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Make levels for Rabbit Escape using the level editor!&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c1aa92ac-2bee-4ef9-b720-c680e931537c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Announcing Smolpxl Scores - a high score table for your game</title>
          <pubDate>Thu, 03 Dec 2020 10:58:34 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/12/03/announcing-smolpxl-scores-a-high-score-table-for-your-game/</link>
          <guid>https://artificialworlds.net/blog/2020/12/03/announcing-smolpxl-scores-a-high-score-table-for-your-game/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/12/03/announcing-smolpxl-scores-a-high-score-table-for-your-game/">&lt;p&gt;It&#x27;s a very early beta for now, but I&#x27;m ready to announce &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;smolpxl-scores&quot;&gt;Smolpxl Scores&lt;&#x2F;a&gt;, which provides high-score tables for Free and Open Source games.&lt;&#x2F;p&gt;
&lt;p&gt;Each game can have multiple high-score tables - for example, you might want one for each level.&lt;&#x2F;p&gt;
&lt;p&gt;At the moment it&#x27;s deployed in my own web hosting and therefore written using the technologies that are most convenient for me to deploy there, which is PHP+MySQL. If it becomes more widely used and the performance suffers I guess I&#x27;ll ask for donations to host it somewhere else, and use more fashionable technologies.&lt;&#x2F;p&gt;
&lt;p&gt;To add a score you make a POST request like this:&lt;&#x2F;p&gt;
&lt;pre&gt;curl https:&#x2F;&#x2F;scores.artificialworlds.net&#x2F;api&#x2F;v1&#x2F;myappname&#x2F;mytablename&#x2F; -d \
    &#x27;{&quot;appId&quot;:&quot;myappid&quot;,&quot;name&quot;:&quot;Megan Tria&quot;, &quot;score&quot;: 13.5, &quot;notes&quot;: &quot;&quot;}&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;and to look at some existing scores you can request them by pages:&lt;&#x2F;p&gt;
&lt;pre&gt;curl &#x27;https:&#x2F;&#x2F;scores.artificialworlds.net&#x2F;api&#x2F;v1&#x2F;myappname&#x2F;mytablename&#x2F;?startRank=11&amp;amp;num=20&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;or by name:&lt;&#x2F;p&gt;
&lt;pre&gt;curl &#x27;https:&#x2F;&#x2F;scores.artificialworlds.net&#x2F;api&#x2F;v1&#x2F;myappname&#x2F;mytablename&#x2F;?startName=David%20Lloyd%20Geo&amp;amp;offset=-5&amp;amp;num=10&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;The results are ordered by players&#x27; scores, and are provided as JSON.&lt;&#x2F;p&gt;
&lt;p&gt;Each table stores only one score per player.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the API will evolve over time, but I hope that what I have now will be good enough to support some real-life games, and provide enough feedback to make it better.&lt;&#x2F;p&gt;
&lt;p&gt;As soon as people are actually using it, I will ensure the current API version (v1) remains stable, and release any incompatible updates as later versions.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to use Smolpxl Scores to add a high-score table to your game, please create an issue at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;smolpxl&#x2F;smolpxl-scores&#x2F;-&#x2F;issues&quot;&gt;gitlab.com&#x2F;smolpxl&#x2F;smolpxl-scores&#x2F;-&#x2F;issues&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This service is only available to Free and Open Source games. Also, if someone abuses it (accidentally or on purpose) I will talk to them, and may eventually have to remove their access if we can&#x27;t fix the problem.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Dovecot not working after upgrade to Ubuntu 20.04.1 (dh key too small)</title>
          <pubDate>Wed, 25 Nov 2020 09:15:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/11/25/dovecot-not-working-after-upgrade-to-ubuntu-20-04-1-dh-key-too-small/</link>
          <guid>https://artificialworlds.net/blog/2020/11/25/dovecot-not-working-after-upgrade-to-ubuntu-20-04-1-dh-key-too-small/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/11/25/dovecot-not-working-after-upgrade-to-ubuntu-20-04-1-dh-key-too-small/">&lt;p&gt;I upgraded to Ubuntu 20.04.1 and chose to keep my existing config files, and my mail server stopped working. In the log I saw:&lt;&#x2F;p&gt;
&lt;pre&gt;Nov 25 09:07:57 machine dovecot: imap-login: Error: Failed to initialize SSL server context: Can&#x27;t load DH parameters: error:1408518A:SSL routines:ssl3_ctx_ctrl:dh key too small: user=&amp;lt;&amp;gt;, rip=someip, lip=someip, session=&amp;lt;someid&amp;gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I was able to fix this by modifying &#x2F;etc&#x2F;dovecot&#x2F;conf.d&#x2F;10-ssl.conf and adding this line:&lt;&#x2F;p&gt;
&lt;pre&gt;ssl_dh = &amp;lt;&#x2F;usr&#x2F;share&#x2F;dovecot&#x2F;dh.pem&lt;&#x2F;pre&gt;
&lt;p&gt;Please let me know if I&#x27;ve introduced an horrific security bug, won&#x27;t you?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP on the overseas aid budget.</title>
          <pubDate>Wed, 25 Nov 2020 08:51:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/11/25/letter-to-my-mp-on-the-overseas-aid-budget/</link>
          <guid>https://artificialworlds.net/blog/2020/11/25/letter-to-my-mp-on-the-overseas-aid-budget/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/11/25/letter-to-my-mp-on-the-overseas-aid-budget/">&lt;p&gt;Letter I sent to my MP today on the overseas aid budget. Let&#x27;s not be foolish.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dear Ben Spencer, Please use your influence to persuade the government to maintain our overseas aid budget commitment at 0.7% of national income.
I believe that changing this policy would be a mistake, increasing the risks of extremism and forced migration around the world. The policy was established when the budget was very tight, and I think the reasons for it remain compelling: to prevent selfishness and short-termism from hurting our own and others&#x27; interests. Please pass my letter on to the Chancellor. Yours sincerely,
Andy Balaam&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Feel free to re-use some or all of this.&lt;&#x2F;p&gt;
&lt;p&gt;To write to your MP, try &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&quot;&gt;writetothem.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Profile a Java unit test (very quickly, with no external tools)</title>
          <pubDate>Mon, 23 Nov 2020 11:03:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/11/23/profile-a-java-unit-test-very-quickly-with-no-external-tools/</link>
          <guid>https://artificialworlds.net/blog/2020/11/23/profile-a-java-unit-test-very-quickly-with-no-external-tools/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/11/23/profile-a-java-unit-test-very-quickly-with-no-external-tools/">&lt;p&gt;I have a unit test that is running slowly, and I want a quick view of what is happening.&lt;&#x2F;p&gt;
&lt;p&gt;I can get a nice overview of where the code spends its time by adding this to the JVM arguments:&lt;&#x2F;p&gt;
&lt;pre&gt;-agentlib:hprof=cpu=samples,lineno=y,depth=3,file=hprof.samples.txt&lt;&#x2F;pre&gt;
&lt;p&gt;and running the test as normal.&lt;&#x2F;p&gt;
&lt;p&gt;Now I can look at the file that was created, hprof.samples.txt, and looking at the bottom section I can see how much time is spent in each method.&lt;&#x2F;p&gt;
&lt;p&gt;This worked for me within IntelliJ IDEA community edition by clicking &quot;Run&quot; then &quot;Edit Configurations&quot; and adding the above code to &quot;VM options&quot; for my test.&lt;&#x2F;p&gt;
&lt;p&gt;It should also work in Gradle by editing gradle.properties and adding something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;org.gradle.jvmargs=-agentlib:hprof=cpu=samples,lineno=y,depth=3,file=hprof.samples.txt&lt;&#x2F;pre&gt;
&lt;p&gt;and should also work in Maven. In fact, I found this information in this stackoverflow question: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;45885434&#x2F;how-do-you-run-maven-unit-tests-with-hprof&quot;&gt;How do you run maven unit tests with hprof?&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why a Free Software web games site?</title>
          <pubDate>Tue, 03 Nov 2020 09:06:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/11/03/why-a-free-software-web-games-site/</link>
          <guid>https://artificialworlds.net/blog/2020/11/03/why-a-free-software-web-games-site/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/11/03/why-a-free-software-web-games-site/">&lt;p&gt;Recently I&#x27;ve been having a lot of fun working on &lt;a href=&quot;&#x2F;&#x2F;smolpxl.artificialworlds.net&quot;&gt;Smolpxl&lt;&#x2F;a&gt;, which is a web site featuring some little retro web games that are all Free and Open Source Software.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a sneak preview of the game I am working on:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rightwaves-2.gif&quot; alt=&quot;A pixellated spaceship avoids some walls, then crashes into them&quot; width=&quot;400&quot; height=&quot;400&quot; class=&quot;aligncenter size-full wp-image-2983&quot;&gt;
&lt;p&gt;Why do this?&lt;&#x2F;p&gt;
&lt;p&gt;Apart from the fact that it&#x27;s fun, I also think there is a need for a site like this: a safe place for kids to play little games without creepy advertising looking over their shoulder, and perverse incentives for the site creators.&lt;&#x2F;p&gt;
&lt;p&gt;Little web games can be a diversion during train journeys, helpful distractions for parents and teachers to provide for kids, and even be a little educational around mouse and keyboard use. I&#x27;ve seen the sites that already exist be helpful in all those contexts, but I&#x27;ve always felt uncomfortable that these sites are supported by advertising, which always comes with concerns about privacy, and also leads game creators to focus on &quot;engagement&quot;, creating mechanisms like site-wide currencies and gambling-style rewards that drive addictive behaviours.&lt;&#x2F;p&gt;
&lt;p&gt;Wouldn&#x27;t it be nice if we in the Free and Open Source community could write some fun games that are free from those unhealthy influences?&lt;&#x2F;p&gt;
&lt;p&gt;Wouldn&#x27;t it be even nicer if we took the opportunity to encourage kids to learn how to make games as well as play them?&lt;&#x2F;p&gt;
&lt;p&gt;Well, that&#x27;s the idea. Have a look at &lt;a href=&quot;&#x2F;&#x2F;smolpxl.artificialworlds.net&quot;&gt;smolpxl.artificialworlds.net&lt;&#x2F;a&gt;, play a few games, and think about writing a few more...&lt;&#x2F;p&gt;
&lt;p&gt;Also, if you know of existing Free and Open Source web games that might work well on the site, let me know and I&#x27;ll have a chat with their creators: I definitely plan to include games by more people than just me.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Code your first game: Snake in JavaScript (on Raspberry Pi)</title>
          <pubDate>Sun, 11 Oct 2020 07:35:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/10/11/code-your-first-game-snake-in-javascript-on-raspberry-pi/</link>
          <guid>https://artificialworlds.net/blog/2020/10/11/code-your-first-game-snake-in-javascript-on-raspberry-pi/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/10/11/code-your-first-game-snake-in-javascript-on-raspberry-pi/">&lt;p&gt;Welcome! We are going to code a whole snake game. It&#x27;s going to look like this:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;33-finished.gif&quot; alt=&quot;A finished snake game being played&quot; width=&quot;480&quot; height=&quot;510&quot; class=&quot;alignnone size-full wp-image-2920&quot;&gt;
&lt;p&gt;It doesn&#x27;t matter if you have never written any code before: I am going to try and explain everything from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to assume you are using a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;&quot;&gt;Raspberry Pi&lt;&#x2F;a&gt;. but you don&#x27;t need one - all you need is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;downloads&quot;&gt;Git&lt;&#x2F;a&gt;, a text editor (like Notepad on Windows) and a web browser (like Firefox or Chrome or Safari or Edge). All laptops and desktop PCs should have the last two. On a tablet or phone it will be more tricky: try to use a Raspberry Pi or other computer if you can. If you are &lt;em&gt;not&lt;&#x2F;em&gt; using a Raspberry Pi, the screenshots will look a bit different, so you&#x27;ll have to do a little detective work to follow along.&lt;&#x2F;p&gt;
&lt;p&gt;For the first part where we download some code, your Raspberry Pi needs to be connected to the Internet, but once the &quot;git clone&quot; part is done, you can do everything else disconnected.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;follow-along-with-the-video&quot;&gt;Follow along with the video&lt;&#x2F;h2&gt;
&lt;iframe title=&quot;Code your first game: Snake in JavaScript (on Raspberry Pi)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4738362d-c5d8-4fa0-a85e-3dc2d59b4763&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;h2 id=&quot;before-we-start&quot;&gt;Before we start&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve got a Raspberry Pi with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;downloads&#x2F;&quot;&gt;Raspberry Pi OS&lt;&#x2F;a&gt; on it, you are ready to go!&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re on another computer, make sure you&#x27;ve got &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;downloads&quot;&gt;Git&lt;&#x2F;a&gt; installed. This should be easy on Linux, and possible on Mac or Windows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up&quot;&gt;Setting up&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing we need to do is download the code we are going to use to help us make a game. We&#x27;ll get it from my games web site, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl games&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Start your web browser:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;01-click-browser.png&quot; alt=&quot;Click the browser launch button&quot; width=&quot;192&quot; height=&quot;122&quot; class=&quot;alignnone size-full wp-image-2924&quot;&gt;
&lt;p&gt;type in the address: smolpxl.artificialworlds.net&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;02-type-smolpxl-address.png&quot; alt=&quot;Typing the address into the browser address bar&quot; width=&quot;503&quot; height=&quot;228&quot; class=&quot;alignnone size-full wp-image-2926&quot;&gt;
&lt;p&gt;and press Enter.&lt;&#x2F;p&gt;
&lt;p&gt;Scroll to the bottom of the page and click the link &quot;make your own games at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&lt;&#x2F;a&gt;&quot;.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;03-scroll-down-choose-gitlab-link.png&quot; alt=&quot;Choosing the gitlab link at the bottom of the page&quot; width=&quot;496&quot; height=&quot;142&quot; class=&quot;alignnone size-full wp-image-2927&quot;&gt;
&lt;p&gt;On the GitLab page that loads, click on Clone on the right:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;04-click-clone.png&quot; alt=&quot;Clicking the Clone button&quot; width=&quot;795&quot; height=&quot;459&quot; class=&quot;alignnone size-full wp-image-2928&quot;&gt;
&lt;p&gt;and, under &quot;Clone with HTTPS&quot; click the &quot;Copy URL&quot; button:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;05-copy-https-url.png&quot; alt=&quot;Clicking the Copy URL button under Clone with HTTPS&quot; width=&quot;369&quot; height=&quot;238&quot; class=&quot;alignnone size-full wp-image-2929&quot;&gt;
&lt;p&gt;Now start the Terminal:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;06-click-terminal.png&quot; alt=&quot;Starting the Terminal by clicking its icon&quot; width=&quot;438&quot; height=&quot;220&quot; class=&quot;alignnone size-full wp-image-2930&quot;&gt;
&lt;p&gt;and type in &quot;git clone&quot; followed by a space, but don&#x27;t press Enter yet. Right-click in the Terminal and choose &quot;Paste&quot; to paste in the URL that we copied before.&lt;&#x2F;p&gt;
&lt;p&gt;Your Terminal should look like this:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;07-git-clone-rightclick-paste.png&quot; alt=&quot;The Terminal showing text: git clone https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl.git&quot; width=&quot;583&quot; height=&quot;123&quot; class=&quot;alignnone size-full wp-image-2931&quot;&gt;
&lt;p&gt;Press Enter, and it should change to look like this:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;08-clone-success.png&quot; alt=&quot;Results of a successful git clone command&quot; width=&quot;572&quot; height=&quot;154&quot; class=&quot;alignnone size-full wp-image-2932&quot;&gt;
&lt;p&gt;Go to your file manager:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;09-click-file-manager.png&quot; alt=&quot;Opening file manager by clicking its icon&quot; width=&quot;418&quot; height=&quot;122&quot; class=&quot;alignnone size-full wp-image-2933&quot;&gt;
&lt;p&gt;and double-click on &quot;smolpxl&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;10-double-click-smolpxl.png&quot; alt=&quot;Double-clicking smolpxl folder&quot; width=&quot;791&quot; height=&quot;422&quot; class=&quot;alignnone size-full wp-image-2934&quot;&gt;
&lt;p&gt;and then double-click on &quot;public&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;11-double-click-public.png&quot; alt=&quot;Double-clicking public folder&quot; width=&quot;471&quot; height=&quot;272&quot; class=&quot;alignnone size-full wp-image-2935&quot;&gt;
&lt;p&gt;We are going to copy the &quot;snake&quot; folder. Right-click on it:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;12-right-click-snake.png&quot; alt=&quot;Right-clicking the snake folder&quot; width=&quot;420&quot; height=&quot;192&quot; class=&quot;alignnone size-full wp-image-2936&quot;&gt;
&lt;p&gt;and choose &quot;Copy&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;13-choose-copy.png&quot; alt=&quot;Choosing &amp;quot;Copy&amp;quot;&quot; width=&quot;488&quot; height=&quot;385&quot; class=&quot;alignnone size-full wp-image-2937&quot;&gt;
&lt;p&gt;then right-click somewhere in the space below, and click &quot;Paste&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;14-right-click-paste.png&quot; alt=&quot;Choosing Paste&quot; width=&quot;306&quot; height=&quot;228&quot; class=&quot;alignnone size-full wp-image-2938&quot;&gt;
&lt;p&gt;The Pi doesn&#x27;t want to paste the new one over the old one, so type a new name for it: &quot;minisnake&quot;, and click &quot;Rename&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;15-type-minisnake-click-rename.png&quot; alt=&quot;Renaming pasted folder to minisnake&quot; width=&quot;508&quot; height=&quot;393&quot; class=&quot;alignnone size-full wp-image-2939&quot;&gt;
&lt;p&gt;Now go into your new minisnake folder by double-clicking on it:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;16-double-click-minisnake.png&quot; alt=&quot;Double-clicking the new minisnake folder&quot; width=&quot;429&quot; height=&quot;262&quot; class=&quot;alignnone size-full wp-image-2941&quot;&gt;
&lt;p&gt;We&#x27;re going to edit the file called game.js. To do this, double-click on it:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;17-double-click-game-js.png&quot; alt=&quot;Double-clicking game.js&quot; width=&quot;407&quot; height=&quot;223&quot; class=&quot;alignnone size-full wp-image-2942&quot;&gt;
&lt;p&gt;This should open the code in a text editor program called Mousepad. (If you&#x27;re not on a Raspberry Pi, you might need to right-click the file and choose &quot;Edit&quot;, or similar, to open it in a text editor.)&lt;&#x2F;p&gt;
&lt;p&gt;We want to delete everything in here and start again. Click &quot;Edit&quot;, then &quot;Select all&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;18-edit-select-all.png&quot; alt=&quot;Choosing Edit, then Select All, and hello to you if you are reading this!&quot; width=&quot;447&quot; height=&quot;343&quot; class=&quot;alignnone size-full wp-image-2943&quot;&gt;
&lt;p&gt;Now press the &quot;Delete&quot; key, and all the code should be gone:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;19-press-delete.png&quot; alt=&quot;Mousepad with all the code gone&quot; width=&quot;424&quot; height=&quot;158&quot; class=&quot;alignnone size-full wp-image-2944&quot;&gt;
&lt;p&gt;We are ready to start!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-game-that-does-nothing&quot;&gt;A game that does nothing&lt;&#x2F;h2&gt;
&lt;p&gt;First, we&#x27;re going to type some code that gets us ready, but doesn&#x27;t actually do anything useful.&lt;&#x2F;p&gt;
&lt;p&gt;Type in this code:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;const game = new Smolpxl.Game();
game.setSize(20, 20);
game.setTitle(&quot;Minisnake&quot;);

function update(runningGame, model) {
}

function view(screen, model) {
}

game.start(&quot;minisnake&quot;, {}, view, update);&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This sets the size of our screen (20 by 20 squares), and a title, and gets things ready for our game. Notice that we start our game by writing game.start.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;ve typed in the code:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;20-type-new-game-code.png&quot; alt=&quot;Mousepad with the code I just told you to type in&quot; width=&quot;493&quot; height=&quot;286&quot; class=&quot;alignnone size-full wp-image-2945&quot;&gt;
&lt;p&gt;click &quot;File&quot; and then &quot;Save&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;21-file-save.png&quot; alt=&quot;Choosing &amp;quot;File&amp;quot;, then &amp;quot;Save&amp;quot;&quot; width=&quot;519&quot; height=&quot;277&quot; class=&quot;alignnone size-full wp-image-2946&quot;&gt;
&lt;p&gt;Now we&#x27;re going to try it out! Go back to the minisnake folder:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;22-choose-minisnake-folder.png&quot; alt=&quot;Switching back to file manager, which is on the minisnake folder still&quot; width=&quot;411&quot; height=&quot;159&quot; class=&quot;alignnone size-full wp-image-2947&quot;&gt;
&lt;p&gt;and double-click on the file called index.html:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;23-double-click-index-html.png&quot; alt=&quot;Double-clicking index.html&quot; width=&quot;375&quot; height=&quot;246&quot; class=&quot;alignnone size-full wp-image-2948&quot;&gt;
&lt;p&gt;If you typed everything correctly, a title screen should appear. To figure out any problems, we will want the Developer tools open. To do that, click the three dots in the top right, then &quot;More tools&quot;, and &quot;Developer tools&quot;:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;24-open-dev-tools.png&quot; alt=&quot;Choosing Chromium&#x27;s burger menu in the top right, then clicking More tools, then Developer Tools&quot; width=&quot;808&quot; height=&quot;571&quot; class=&quot;alignnone size-full wp-image-2949&quot;&gt;
&lt;p&gt;The tool we want is the Console, so click &quot;Console&quot; to see it:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;25-click-console.png&quot; alt=&quot;Choosing the Console tab in Developer tools&quot; width=&quot;440&quot; height=&quot;183&quot; class=&quot;alignnone size-full wp-image-2950&quot;&gt;
&lt;p&gt;If everything went well, the Console should be empty:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;26-console-should-be-empty.png&quot; alt=&quot;Our game on the left, and the Console on the right, with no error messages showing&quot; width=&quot;803&quot; height=&quot;318&quot; class=&quot;alignnone size-full wp-image-2951&quot;&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-01-nothing.js&quot;&gt;game-01-nothing.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now we have everything ready to get started, and you can see our game already has a title screen!&lt;&#x2F;p&gt;
&lt;p&gt;The game is going to be made from three things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;a model,&lt;&#x2F;li&gt;
&lt;li&gt;a view, and&lt;&#x2F;li&gt;
&lt;li&gt;an update function.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s start with the model and the view.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-model-and-the-view&quot;&gt;The Model and the View&lt;&#x2F;h2&gt;
&lt;p&gt;The first thing we&#x27;re going to do is add an apple to the game. To do that, we need to do two things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;say where the apple is, and&lt;&#x2F;li&gt;
&lt;li&gt;draw the apple on the screen.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To say where the apple is, we need a model. We actually already have a model, but it&#x27;s just empty.&lt;&#x2F;p&gt;
&lt;p&gt;Switch back to your code:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;27-back-to-mousepad.png&quot; alt=&quot;Switching back to Mousepad by clicking game.js in the top bar&quot; width=&quot;369&quot; height=&quot;147&quot; class=&quot;alignnone size-full wp-image-2953&quot;&gt;
&lt;p&gt;Look at the last line of code we typed, and find where you typed {}. This is the &quot;model&quot;, or the state of out game when we start it off. Now, we&#x27;re going to replace that with newModel(), which means call a function called newModel. (A function is a piece of code that we can re-use, and &quot;calling&quot; a function means running that code.)&lt;&#x2F;p&gt;
&lt;p&gt;So we&#x27;re going to write a function called newModel, and then call it. Replace the very last line of your code with this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;function newModel() {
    return {
        apple: [5, 5]
    };
}&lt;&#x2F;span&gt;

game.start(&quot;minisnake&quot;, &lt;span style=&quot;color: green;&quot;&gt;newModel()&lt;&#x2F;span&gt;, view, update);
&lt;&#x2F;pre&gt;
&lt;p&gt;The top part makes a function called newModel, and the bottom part is the same as you had before, except {} is replaced by newModel().&lt;&#x2F;p&gt;
&lt;p&gt;Have another look at the newModel function we wrote (the top part). Can you see where we made the apple? After the word return, we have some stuff inside curly brackets ({ and later, }). When we use curly brackets like that, we are making what JavaScript calls an object. Objects have properties. So far, our object only has one property, which is called &quot;apple&quot;. The right-hand part, [5, 5] is how we are say where our apple is - it&#x27;s at the position 5 squares in from the left, and 5 squares down from the top of our screen.&lt;&#x2F;p&gt;
&lt;p&gt;Now we have said where our apple is, we also need to draw it on the screen. That happens inside the view function. Scroll up and find the part that says function view() and add a new line between the opening { and the closing one:&lt;&#x2F;p&gt;
&lt;pre&gt;function view(screen, model) {
    &lt;span style=&quot;color: green;&quot;&gt;screen.set(model.apple[0], model.apple[1], [255, 0, 0]);&lt;&#x2F;span&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;We are calling a function called screen.set, which draws a square on the screen. We are passing in three things to it: two points for the position to draw, and then the colour to use. The position is model.apple[0], which means take the first part of the [5, 5] we typed before, and model.apple[1], which means take the second part. The color is [255, 0, 0] which means lots of red, and no green or blue, because this is a red-green-blue (RGB) colour.&lt;&#x2F;p&gt;
&lt;p&gt;So let&#x27;s try this out and see whether the apple is drawn on the screen. First save your code in Mousepad, then switch back to our game (in Chromium), and click the refresh button to reload it.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;28-reload-page.png&quot; alt=&quot;Switching back to Chromium in the top bar, then clicking the refresh button&quot; width=&quot;462&quot; height=&quot;201&quot; class=&quot;alignnone size-full wp-image-2954&quot;&gt;
&lt;p&gt;You should see your game saying &quot;Enter to start&quot;. Click on it, and the game should start, and look like this:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;29-just-apple.png&quot; alt=&quot;The game window is black, except there is a red square for the apple&quot; width=&quot;492&quot; height=&quot;252&quot; class=&quot;alignnone size-full wp-image-2955&quot;&gt;
&lt;p&gt;Well done, you have drawn an apple!&lt;&#x2F;p&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-02-just-apple.js&quot;&gt;game-02-just-apple.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drawing-a-snake&quot;&gt;Drawing a snake&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have an apple, let&#x27;s follow the exact same pattern to add a snake. We&#x27;re going to add information about it to the model, and then use that information to draw it on the screen.&lt;&#x2F;p&gt;
&lt;p&gt;First, change the newModel function to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function newModel() {
    return {
        apple: [5, 5]&lt;span style=&quot;color: green;&quot;&gt;,
        body: [[10, 10], [10, 11], [10, 12], [10, 13], [10, 14]]&lt;&#x2F;span&gt;
    };
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t miss the extra comma at end of the &quot;apple&quot; line!&lt;&#x2F;p&gt;
&lt;p&gt;To give the co-ordinates of the apple, we just used one [x, y] pair. Because the snake&#x27;s body is made from 5 points, we need 5 similar pairs.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve described the snake&#x27;s body position, so now let&#x27;s draw it on the screen. Update the view function so it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function view(screen, model) {
    &lt;span style=&quot;color: green;&quot;&gt;&#x2F;&#x2F; Snake
    for (const [x, y] of model.body) {
        screen.set(x, y, [0, 255, 0]);
    }

    &#x2F;&#x2F; Apple&lt;&#x2F;span&gt;
    screen.set(model.apple[0], model.apple[1], [255, 0, 0]);
}
&lt;&#x2F;pre&gt;
&lt;p&gt;We have added a for loop - it runs through all the points in body, and for each one it draws a square on the screen. This time the colour is [0, 255, 0], which means the snake will be green.&lt;&#x2F;p&gt;
&lt;p&gt;(By the way, did you notice the lines we added that start with &#x2F;&#x2F; - these are &quot;comments&quot; - we can write anything we like after the slashes and it doesn&#x27;t do anything. We can use comments to add notes that help us remember what different bits of our program do.&lt;&#x2F;p&gt;
&lt;p&gt;Save the file, go back to the game in Chromium and click the Refresh button again. If all goes well, you should see the snake appear:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;29-snake-and-apple.png&quot; alt=&quot;A black game screen, with a red dot and green line for the snake&quot; width=&quot;553&quot; height=&quot;441&quot; class=&quot;alignnone size-full wp-image-2956&quot;&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-03-snake.js&quot;&gt;game-03-snake.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So now we have a snake and an apple, but nothing is really happening ... let&#x27;s fix that next.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;making-the-snake-move&quot;&gt;Making the snake move&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve made a model in newModel and we&#x27;ve drawn it on the screen in view, but how do we make things move around? That is where the update function comes in: this is where we change the model based on what is happening.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start by making the snake move forward forever. Change the update function to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function update(runningGame, model) {
    &lt;span style=&quot;color: green;&quot;&gt;&#x2F;&#x2F; Move the snake
    let newHead = Smolpxl.coordMoved(model.body[0], model.dir);
    let newTail = model.body.slice(0, -1);

    model.body = [newHead, ...newTail];
    return model;&lt;&#x2F;span&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;and change the newModel function to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function newModel() {
    return {
        apple: [5, 5],
        body: [[10, 10], [10, 11], [10, 12], [10, 13], [10, 14]]&lt;span style=&quot;color: green;&quot;&gt;,
        dir: Smolpxl.directions.UP&lt;&#x2F;span&gt;
    };
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Again, notice the comma at the end of the body line!&lt;&#x2F;p&gt;
&lt;p&gt;We added dir to the model, which is the direction the snake is facing.&lt;&#x2F;p&gt;
&lt;p&gt;The update function makes newHead by moving the snake&#x27;s head (the first entry in its body, which it gets with model.body[0]) in the direction it is facing (model.dir). Then we create newTail, which is everything in the old model.body except the last entry in the list (this is what .slice(0, -1) means).&lt;&#x2F;p&gt;
&lt;p&gt;Finally we update the body by setting it to [newHead, ...newTail], which just means make a new list by sticking newHead on to the beginning of newTail.&lt;&#x2F;p&gt;
&lt;p&gt;Save, switch to the game in Chromium, and refresh. Because we set dir to Smolpxl.directions.UP inside newModel, the snake moves updards!&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;30-snake-moving.gif&quot; alt=&quot;The green snake moves up the screen (and off the top)&quot; width=&quot;600&quot; height=&quot;430&quot; class=&quot;alignnone size-full wp-image-2957&quot;&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-04-movement.js&quot;&gt;game-04-movement.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If the snake disappears off the top, click the refresh button to see it again.&lt;&#x2F;p&gt;
&lt;p&gt;The game isn&#x27;t too much fun yet. Let&#x27;s add some keyboard controls, and allow you to die when you go off-screen.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;controlling-the-snake&quot;&gt;Controlling the snake&lt;&#x2F;h2&gt;
&lt;p&gt;The update function moves the snake, but now it&#x27;s time to make it a bit cleverer, by changing direction when you press a key, and stopping you when you go off-screen. Change it so it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function update(runningGame, model) {
    &lt;span style=&quot;color: green;&quot;&gt;if (!model.alive) {
        return;
    }

    if (runningGame.receivedInput(&quot;LEFT&quot;))  {
        model.dir = Smolpxl.directions.LEFT;
    } else if (runningGame.receivedInput(&quot;RIGHT&quot;)) {
        model.dir = Smolpxl.directions.RIGHT;
    } else if (runningGame.receivedInput(&quot;UP&quot;)) {
        model.dir = Smolpxl.directions.UP;
    } else if (runningGame.receivedInput(&quot;DOWN&quot;)) {
        model.dir = Smolpxl.directions.DOWN;
    }&lt;&#x2F;span&gt;

    &#x2F;&#x2F; Move the snake
    let newHead = Smolpxl.coordMoved(model.body[0], model.dir);
    let newTail = model.body.slice(0, -1);

    &lt;span style=&quot;color: green;&quot;&gt;&#x2F;&#x2F; Die if we hit the edge
    if (
        newHead[0] === runningGame.screen.minX ||
        newHead[0] === runningGame.screen.maxX ||
        newHead[1] === runningGame.screen.minY ||
        newHead[1] === runningGame.screen.maxY
    ) {
        model.alive = false;
    }&lt;&#x2F;span&gt;

    model.body = [newHead, ...newTail];
    return model;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;and change the newModel function to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function newModel() {
    return {
        &lt;span style=&quot;color: green;&quot;&gt;alive: true,&lt;&#x2F;span&gt;
        apple: [5, 5],
        body: [[10, 10], [10, 11], [10, 12], [10, 13], [10, 14]],
        dir: Smolpxl.directions.UP
    };
}
&lt;&#x2F;pre&gt;
&lt;p&gt;We keep track of whether the snake is alive in the model, and we immediately return from update if we are dead, meaning the snake stops moving. (The ! in if (!model.alive) means &quot;not&quot;, so we are saying what to do when we are not alive - when we are dead. The return here means immediately stop running the code in this function.)&lt;&#x2F;p&gt;
&lt;p&gt;The next new part of update allows us to check whether an arrow key was pressed (using the runningGame.receivedInput function), and if so, change the direction of the snake (model.dir).&lt;&#x2F;p&gt;
&lt;p&gt;Finally, nearer the end of update, we check whether the position of the snake&#x27;s head (newHead) is at one of the screen edges, by comparing its co-ordinates with the maximum and minimum co-ordinates on the screen. If we are off the edge, we set model.alive to false, meaning the snake is now dead.&lt;&#x2F;p&gt;
&lt;p&gt;Save, switch to the game in Chromium, and refresh. With all that, we can control the snake with the arrow keys, and it can die:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;31-controlling-movement.gif&quot; alt=&quot;A snake moves around, turning because arrow keys were used to control it&quot; width=&quot;480&quot; height=&quot;510&quot; class=&quot;alignnone size-full wp-image-2958&quot;&gt;
&lt;p&gt;Try clicking on your game and then pressing the arrow keys to control the snake.&lt;&#x2F;p&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-05-control.js&quot;&gt;game-05-control.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is kind-of a game, but surely it&#x27;s time to eat some apples?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;eating-apples&quot;&gt;Eating apples&lt;&#x2F;h2&gt;
&lt;p&gt;If we&#x27;re going to be eating apples, they should probably not always be in the same place, right?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start off by making a brand new function. Make some space immediately above the update function, and type in this code:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;function randApple() {
    return [Smolpxl.randomInt(1, 18), Smolpxl.randomInt(1, 18)];
}&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This function gives us some random co-ordinates where we can place an apple. Update newModel to place the first apple at a random place, by making it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;function newModel() {
    return {
        alive: true,
        apple: &lt;span style=&quot;color: green;&quot;&gt;randApple()&lt;&#x2F;span&gt;,
        body: [[10, 10], [10, 11], [10, 12], [10, 13], [10, 14]],
        dir: Smolpxl.directions.UP
    };
}
&lt;&#x2F;pre&gt;
&lt;p&gt;So instead of writing the exact co-ordinates we want ([5, 5]), now we&#x27;re calling our new randApple function, which gives us back some random co-ordinates.&lt;&#x2F;p&gt;
&lt;p&gt;Now we can place apples randomly, let&#x27;s change the update function to allow us to eat apples. While we&#x27;re there, let&#x27;s check whether we crashed into our own body too:&lt;&#x2F;p&gt;
&lt;pre&gt;function update(runningGame, model) {
    &lt;span style=&quot;color: blue;&quot;&gt;... All the same stuff as before ...&lt;&#x2F;span&gt;

    &#x2F;&#x2F; Die if we hit the edge
    if (
        newHead[0] === runningGame.screen.minX ||
        newHead[0] === runningGame.screen.maxX ||
        newHead[1] === runningGame.screen.minY ||
        newHead[1] === runningGame.screen.maxY
    ) {
        model.alive = false;
    }

    &lt;span style=&quot;color: green;&quot;&gt;&#x2F;&#x2F; If we hit the apple we get longer
    if (Smolpxl.equalArrays(newHead, model.apple)) {
        for (let i = 0; i &amp;lt; 5; i++) {
            newTail.push([-1, -1]);
        }
        model.apple = randApple();
    }

    &#x2F;&#x2F; If we hit our own body, we die
    if (Smolpxl.arrayIncludesArray(newTail, newHead)) {
        model.alive = false;
    }&lt;&#x2F;span&gt;

    model.body = [newHead, ...newTail];
    return model;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;We use the Smolpxl.equalArrays function* to ask whether the new position of the snake&#x27;s head (newHead) is the same as the position of the apple (model.apple).&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;* The function is called &quot;equalArrays&quot; because both newHead and model.apple are lists of two co-ordinates (x and y), so we are storing them inside JavaScript &quot;arrays&quot;. You can spot an array because it is surrounded by square brackets ([).&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If the head is on top of the apple, we do two things: add new items to the end of newTail, by using a for loop that runs 5 times. Each time it runs, it uses push to add another body part on the to the tail. (It actually adds an off-screen point [-1, -1] every time - this means they won&#x27;t get drawn at first, but as the snake moves forward, they will gradually get replaced by on-screen points, and we&#x27;ll see the snake gradually get longer.)&lt;&#x2F;p&gt;
&lt;p&gt;The second thing we do if the head is on the apple is move the apple by setting model.apple to another random point given to us by randApple.&lt;&#x2F;p&gt;
&lt;p&gt;We also check whether we have hit our own body using the Smolpxl.arrayIncludesArray function (asking whether newHead is the same point as one of the points in newTail) and set model.alive to false, meaning we&#x27;re dead, if so.&lt;&#x2F;p&gt;
&lt;p&gt;Save, switch to the game in Chromium, and refresh. Now we can really play a game of snake!&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;32-eating-apples.gif&quot; alt=&quot;A snake moves around the screen, eating apples and getting longer when it does&quot; width=&quot;480&quot; height=&quot;510&quot; class=&quot;alignnone size-full wp-image-2959&quot;&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-06-eating-apples.js&quot;&gt;game-06-eating-apples.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We are nearly done. The last jobs are to make the game look a little nicer, and handle starting a new game after we crash.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;finishing-off&quot;&gt;Finishing off&lt;&#x2F;h2&gt;
&lt;p&gt;We need to draw the walls around the edge that you crash into, and it would also be nice to show your score at the top of the screen. We can do both of these by adding a bit more at the beginning of the view function:&lt;&#x2F;p&gt;
&lt;pre&gt;function view(screen, model) {
    &lt;span style=&quot;color: green;&quot;&gt;screen.messageTopLeft(`Score: ${model.body.length}`);

    &#x2F;&#x2F; Walls
    for (const x of screen.xs()) {
        screen.set(x, screen.minY, [150, 150, 150]);
        screen.set(x, screen.maxY, [150, 150, 150]);
    }
    for (const y of screen.ys()) {
        screen.set(screen.minX, y, [150, 150, 150]);
        screen.set(screen.maxX, y, [150, 150, 150]);
    }&lt;&#x2F;span&gt;

    &lt;span style=&quot;color: blue;&quot;&gt;... All the same stuff as before ...&lt;&#x2F;span&gt;
}
&lt;&#x2F;pre&gt;
&lt;p&gt;We use the screen.messageTopLeft function to display a message at the top. To format the message we write some text inside backticks (`), which allows us to substitute values in. Here we typed ${model.body.length}, which means our score is how many points there are inside our body (which is the length of the array model.body). The ${} part just means &quot;substitute this value in here&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;We make use of the screen.xs() and screen.ys() functions that give us a list of all the x and y co-ordinates on the screen to draw the wall.&lt;&#x2F;p&gt;
&lt;p&gt;Save, switch back to the game, and refresh to see how this looks. If you want to check what you have typed so far, compare it against &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-07-walls-and-score.js&quot;&gt;game-07-walls-and-score.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The very last thing we need to do is handle restarting the game after we crash.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s display a message on the screen by updating the view function one last time:&lt;&#x2F;p&gt;
&lt;pre&gt;function view(screen, model) {
    &lt;span style=&quot;color: blue;&quot;&gt;... All the same stuff as before ...&lt;&#x2F;span&gt;

    &#x2F;&#x2F; Snake
    for (const [x, y] of model.body) {
        screen.set(x, y, [0, 255, 0]);
    }

    &lt;span style=&quot;color: green&quot;&gt;&#x2F;&#x2F; Death
    if (!model.alive) {
        screen.set(model.body[0][0], model.body[0][1], [0, 0, 255]);
        screen.dim();
        const score = model.body.length;
        screen.message([&quot;Well done!&quot;, `Score: ${score}`, &quot;Press &amp;lt;SELECT&amp;gt;&quot;]);
    }&lt;&#x2F;span&gt;

    &#x2F;&#x2F; Apple
    screen.set(model.apple[0], model.apple[1], [255, 0, 0]);
}
&lt;&#x2F;pre&gt;
&lt;p&gt;When the snake is not alive we draw a blue dot ([0, 0, 255]) where its head is, then make the screen dark with the screen.dim function, and write a message on the screen with the screen.message function.&lt;&#x2F;p&gt;
&lt;p&gt;Last, if we press Enter while the death message is visible, we want to go back to the start. We can do that with a small change to the update function:&lt;&#x2F;p&gt;
&lt;pre&gt;function update(runningGame, model) {
    if (!model.alive) {
        &lt;span style=&quot;color: green;&quot;&gt;if (runningGame.receivedInput(&quot;SELECT&quot;)) {
            runningGame.endGame();
            return newModel();
        } else {
            return;
        }&lt;&#x2F;span&gt;
    }

    if (runningGame.receivedInput(&quot;LEFT&quot;))  {
    &lt;span style=&quot;color: blue;&quot;&gt;... All the same stuff as before ...&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that we deleted the line that just said return; and replaced it with the new if code. We check whether the user pressed the SELECT button (which means the Enter key) and if so, we tell Smolpxl to go back to the title screen by calling runningGame.endGame, and then we reset everything in the model by returning newModel().&lt;&#x2F;p&gt;
&lt;p&gt;Save, switch to the game in Chromium, and refresh. Our game is finished!&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;33-finished.gif&quot; alt=&quot;A finished snake game being played&quot; width=&quot;480&quot; height=&quot;510&quot; class=&quot;alignnone size-full wp-image-2920&quot;&gt;
&lt;p&gt;If you see red error messages in the Console, you probably have a typo: double-check everything you typed, and compare it with this version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;examples&#x2F;minisnake&#x2F;game-08-finished.js&quot;&gt;game-08-finished.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;well-done&quot;&gt;Well done!&lt;&#x2F;h2&gt;
&lt;p&gt;If you got that working, you should be extremely pleased with yourself. Typing all that code correctly is a major challenge, and your determination has paid off.&lt;&#x2F;p&gt;
&lt;p&gt;Take a breath, and have a think about whether you can show what you&#x27;ve done to someone else. I&#x27;m pretty sure they will be impressed that you coded an entire game!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;&#x2F;h2&gt;
&lt;p&gt;When you&#x27;ve properly taken the time to enjoy the great work you&#x27;ve done, try reading through the code, and reading back through this blog post to try and understand how the program works.&lt;&#x2F;p&gt;
&lt;p&gt;To learn more about JavaScript and making web sites, you can follow this much more comprehensive tutorial: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;Getting_started_with_the_web&quot;&gt;Getting started with the Web&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ve done really well. If you want an extra challenge, try improving your game using these challenges. Bear in mind though, working these out on your own will be &lt;strong&gt;much&lt;&#x2F;strong&gt; harder than what we&#x27;ve done so far:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Challenge 1: Display a different message if you don&#x27;t eat any apples.&lt;&#x2F;strong&gt; If you crash before eating any apples, you might need some extra encouragement: change the part of the view function where we display a &quot;Well done!&quot; message, and if our score is 5 (if (model.body.length === 5) then display a different message - maybe &quot;Bad luck!&quot;. Look for if ... else statements we have already written, to see how they work.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Challenge 2: Prevent going back on yourself.&lt;&#x2F;strong&gt; Update your code so that when snake is going right, and the user presses left, we ignore it. You will need to change the update function, where we use receivedInput to check what key the user pressed. Instead of just setting the direction, you will need to add a new if statement that check what direction we are facing already.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Challenge 3: Remember the high score.&lt;&#x2F;strong&gt; Remember the best score anyone has got, and display it at the top-right of the screen. At the moment in our code, we return newModel(); in update, when we want to restart the game. That means we have a totally fresh model, forgetting everything else that happened before. If we want to remember a high score, we can&#x27;t do that! If you want some extra inspiration, have a look at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;blob&#x2F;master&#x2F;public&#x2F;snake&#x2F;game.js&quot;&gt;the more complete version of snake&lt;&#x2F;a&gt; that comes with the Smolpxl games. It&#x27;s actually included in the code you downloaded with the git clone command you typed at the very beginning.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;what-next&quot;&gt;What next?&lt;&#x2F;h2&gt;
&lt;p&gt;To learn more, try:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Reading the Smolpxl code that you downloaded at the beginning with git clone. Inside the public folder there are several games to look at, including a more complete version of snake!&lt;&#x2F;li&gt;
&lt;li&gt;Following my video series &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=U4PG2zU70C4&amp;amp;list=PLgyU3jNA6VjSy8d9f_v_azon83OS1Ooyc&quot;&gt;making Smolpxl games&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Doing more JavaScript using my video &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=LcLBGvMOZWE&amp;amp;list=PLgyU3jNA6VjSbjmQRSNoi2IOtydOw9IW-&amp;amp;index=2&quot;&gt;Snowflake Christmas card web page on the Raspberry Pi&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Joining the Smolpxl community by clicking on &quot;Community&quot; at the top of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;smolpxl.artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Writing a new Smolpxl game! If you like it, send it in to be part of Smolpxl, by creating an issue at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;issues&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&#x2F;-&#x2F;issues&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Following the tutorial here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Learn&#x2F;Getting_started_with_the_web&quot;&gt;Getting started with the Web&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Searching for &quot;JavaScript tutorial&quot; on the Internet, or on your favourite video site.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Remember to have fun, and be kind to the people you meet on the way.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Play and create little retro games at Smolpxl</title>
          <pubDate>Fri, 25 Sep 2020 02:12:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/09/25/play-and-create-little-retro-games-at-smolpxl/</link>
          <guid>https://artificialworlds.net/blog/2020/09/25/play-and-create-little-retro-games-at-smolpxl/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/09/25/play-and-create-little-retro-games-at-smolpxl/">&lt;p&gt;I love simple games: playing them and writing them.&lt;&#x2F;p&gt;
&lt;p&gt;But, it can be overwhelming getting started in the complex ecosystems of modern technology.&lt;&#x2F;p&gt;
&lt;p&gt;So, I am writing the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&quot;&gt;Smolpxl library&lt;&#x2F;a&gt;, which is some JavaScript code that makes it quite simple to write simple, pixellated games. It gives you a fixed-size screen to draw on, and it handles your game loop and frames-per-second, so you can focus on two things: updating your game &quot;model&quot; - the world containing all the things that exist in your game, and drawing onto the screen.&lt;&#x2F;p&gt;
&lt;p&gt;I am also writing some games with this library, and publishing them at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;smolpxl.artificialworlds.net&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;smolpxl-main.png&quot; alt=&quot;&quot; width=&quot;658&quot; height=&quot;646&quot; class=&quot;alignnone size-full wp-image-2913&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I am hoping this site will gradually gain more and more Free and Open Source games, and start to rival some of the advertising-supported sites for the attention of casual gamers, especially kids.&lt;&#x2F;p&gt;
&lt;p&gt;Smolpxl is done for fun, and for its educational value, so it should be a safer place for kids than a world of advertising, loot boxes and site-wide currencies.&lt;&#x2F;p&gt;
&lt;p&gt;As I write games, I want to show how easy and fun it can be, so I will be streaming myself live doing it on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.twitch.tv&#x2F;andybalaam&quot;&gt;twitch.tv&#x2F;andybalaam&lt;&#x2F;a&gt;, and putting the recordings up on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;a&#x2F;andybalaam&#x2F;video-channels&quot;&gt;PeerTube&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;ajbalaam&quot;&gt;youtube.com&#x2F;user&#x2F;ajbalaam&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am hoping these videos will serve as tutorials that help other people get into writing simple games.&lt;&#x2F;p&gt;
&lt;p&gt;Would you like to help? If so:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spread the word&lt;&#x2F;strong&gt; by sharing this blog post and sending people to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;smolpxl.artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Write a game&lt;&#x2F;strong&gt;. You can get started by reading the code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&lt;&#x2F;a&gt; or by watching the videos.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Suggest other games&lt;&#x2F;strong&gt;. When their authors are willing, I want to include other Free and Open Source Software web-based games on the site - let me know what I should add.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Join the chat&lt;&#x2F;strong&gt; at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.zulipchat.com&#x2F;&quot;&gt;smolpxl.zulipchat.com&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Follow me&lt;&#x2F;strong&gt; to stay informed - You can follow &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;web&#x2F;accounts&#x2F;7995&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt; on Mastodon, subscribe to this blog&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;feed&#x2F;?dontcache=true&quot;&gt;RSS feed&lt;&#x2F;a&gt;, or find me on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;peertube.mastodon.host&#x2F;accounts&#x2F;andybalaam&quot;&gt;PeerTube&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;ajbalaam&quot;&gt;YouTube&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.twitch.tv&#x2F;andybalaam&quot;&gt;Twitch&lt;&#x2F;a&gt; and even legacy social networks like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;Twitter&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>shareon.js.org now has a Share to Mastodon button</title>
          <pubDate>Thu, 24 Sep 2020 23:32:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/09/24/shareon-js-org-now-has-a-share-to-mastodon-button/</link>
          <guid>https://artificialworlds.net/blog/2020/09/24/shareon-js-org-now-has-a-share-to-mastodon-button/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/09/24/shareon-js-org-now-has-a-share-to-mastodon-button/">&lt;p&gt;I was looking for the right way to make a &quot;Share This&quot;-style button for my tiny games site &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&#x2F;&quot;&gt;Smolpxl&lt;&#x2F;a&gt;, and I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NickKaramoff&#x2F;shareon&quot;&gt;shareon&lt;&#x2F;a&gt; which worked exactly the way I wanted (load the JavaScript and call a function to display the buttons, with no privacy concerns), and looked really nice.&lt;&#x2F;p&gt;
&lt;p&gt;The only thing that was missing was a Mastodon button.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Share to Mastodon&quot; is more complicated than something like Share to Twitter, because Mastodon is not one web site, but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&#x2F;&quot;&gt;lots of web sites that all talk to each other&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So, after someone clicks &quot;Share to Mastodon&quot;, you need to ask them which web site (or Mastodon instance) they meant.&lt;&#x2F;p&gt;
&lt;p&gt;I started out by hacking a Mastodon button in after the shareon ones, and prompting the user for their instance. This was a little unfriendly, but it worked:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;share-mastodon-prompt.gif&quot; alt=&quot;Click Share, Mastodon, enter instance URL into ugly browser prompt, and end up at Mastodon&quot; width=&quot;360&quot; height=&quot;408&quot; class=&quot;alignnone size-full wp-image-2908&quot;&gt;
&lt;p&gt;But luckily I didn&#x27;t stick with that. Because I think shareon is awesome, and because I want more people to use Mastodon, I decided to try &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NickKaramoff&#x2F;shareon&#x2F;pull&#x2F;23&quot;&gt;adding a Mastodon button to shareon&lt;&#x2F;a&gt;. I wrote the code to work similarly to my original hack, and submitted a Pull Request.&lt;&#x2F;p&gt;
&lt;p&gt;I am really glad I did that, because what followed was a really positive Free and Open Source Software mini-interaction. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;karamoff.dev&#x2F;&quot;&gt;Nick Karamov&lt;&#x2F;a&gt; responded with lots of improvements and bug fixes to my original change, and as we discussed the problem more, I expressed the desire for a proper page to choose Mastodon instance, that would be more friendly than a simple prompt. I also said that it would be difficult.&lt;&#x2F;p&gt;
&lt;p&gt;In retrospect, maybe suggesting it would be difficult was a clever trick, because the next thing I knew, Nick had implemented just such a page: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NickKaramoff&#x2F;toot&quot;&gt;toot.karamoff.dev&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Because toot.karamoff.dev now existed, the &quot;Share to Mastodon&quot; button became much simpler: we can send our post information to toot.karamoff.dev, and it asks which Mastodon instance you want to use, and passes it on the correct place.&lt;&#x2F;p&gt;
&lt;p&gt;So my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NickKaramoff&#x2F;shareon&#x2F;pull&#x2F;27&quot;&gt;new Pull Request&lt;&#x2F;a&gt; was much simpler than the original, and with a few more improvements suggested by Nick, it&#x27;s merged and I have a usable Share to Mastodon button without hacking it in.&lt;&#x2F;p&gt;
&lt;p&gt;The cake has a little more icing too, because I was also able to improve toot.karamoff.dev by adding code that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NickKaramoff&#x2F;toot&#x2F;pull&#x2F;1&quot;&gt;downloads the up-to-date list of Mastodon instances&lt;&#x2F;a&gt; from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&quot;&gt;joinmastodon.org&lt;&#x2F;a&gt; and provides them as suggestions, which can be really helpful if you can&#x27;t remember the exact name of your instance. Again, Nick&#x27;s suggestions on my Pull Request were incredibly helpful and made the code way better than what I originally wrote. Now it works really smoothly:&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;share-mastodon2.gif&quot; alt=&quot;Click Share, Mastodon, choose instance from a friendly list on toot, and end up at Mastodon&quot; width=&quot;440&quot; height=&quot;488&quot; class=&quot;alignnone size-full wp-image-2910&quot;&gt;
&lt;p&gt;In a small way, this was a fantastic example of how effective and fun working on Free and Open Source Software can be.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Coding a tiny game in JavaScript video</title>
          <pubDate>Fri, 18 Sep 2020 00:25:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/09/18/coding-a-tiny-game-in-javascript-video/</link>
          <guid>https://artificialworlds.net/blog/2020/09/18/coding-a-tiny-game-in-javascript-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/09/18/coding-a-tiny-game-in-javascript-video/">&lt;p&gt;I&#x27;m working on a little JavaScript library called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;smolpxl&quot;&gt;Smolpxl&lt;&#x2F;a&gt;. It aims to make it really easy to create retro-style pixellated games that run well in the browser, using simple JavaScript.&lt;&#x2F;p&gt;
&lt;p&gt;This is me live-streaming writing a tiny &quot;game&quot; using Smolpxl:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Coding a tiny game in JavaScript&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;42a2e0cd-42af-4139-8b50-dd5c6e169866&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;To play the games or get involved in the community, go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smolpxl.artificialworlds.net&quot;&gt;smolpxl.artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>short - command line tool to truncate lines to fit in the terminal</title>
          <pubDate>Thu, 10 Sep 2020 15:18:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/09/10/short-command-line-tool-to-truncate-lines-to-fit-in-the-terminal/</link>
          <guid>https://artificialworlds.net/blog/2020/09/10/short-command-line-tool-to-truncate-lines-to-fit-in-the-terminal/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/09/10/short-command-line-tool-to-truncate-lines-to-fit-in-the-terminal/">&lt;p&gt;Sometimes I run grep commands that search files with hugely-long lines. If those lines match, they are printed out and spam my terminal with huge amounts of information, that I probably don&#x27;t need.&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t find a tool that limits the line-length of its output, so I wrote a tiny one.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;short&quot;&gt;short&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You use it like this (my typical usage):&lt;&#x2F;p&gt;
&lt;pre&gt;grep foo myfile.txt | short&lt;&#x2F;pre&gt;
&lt;p&gt;Or specify the column width like this:&lt;&#x2F;p&gt;
&lt;pre&gt;short -w 5 myfile.txt&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s written in Rust. Feel free to add features, fix bugs and package it for your operating system&#x2F;distribution at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;short&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;short&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Set the date (EXIF) of a photo on Linux</title>
          <pubDate>Thu, 13 Aug 2020 13:31:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/08/13/set-the-date-exif-of-a-photo-on-linux/</link>
          <guid>https://artificialworlds.net/blog/2020/08/13/set-the-date-exif-of-a-photo-on-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/08/13/set-the-date-exif-of-a-photo-on-linux/">&lt;p&gt;To set the date when a photo was taken, install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;exiftool.org&#x2F;&quot;&gt;ExifTool&lt;&#x2F;a&gt; e.g.:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt install libimage-exiftool-perl   # If on Ubuntu
sudo dnf install perl-Image-ExifTool.noarch  # If on Fedora&lt;&#x2F;pre&gt;
&lt;p&gt;And modify the photo with a command like this:&lt;&#x2F;p&gt;
&lt;pre&gt;exiftool -DateTimeOriginal=&#x27;2020-08-13 12:00 UTC&#x27; myphoto.jpg&lt;&#x2F;pre&gt;
&lt;p&gt;More info on the Exif tags you can edit is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;exiftool.org&#x2F;TagNames&#x2F;EXIF.html&quot;&gt;ExifTool&#x27;s docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to send an SMS using netcat (via SMPP)</title>
          <pubDate>Mon, 10 Aug 2020 15:50:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/08/10/how-to-send-an-sms-using-netcat-via-smpp/</link>
          <guid>https://artificialworlds.net/blog/2020/08/10/how-to-send-an-sms-using-netcat-via-smpp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/08/10/how-to-send-an-sms-using-netcat-via-smpp/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smpp.org&#x2F;&quot;&gt;SMPP&lt;&#x2F;a&gt; is a binary protocol used by phone companies to send text messages, otherwise known as SMS messages.&lt;&#x2F;p&gt;
&lt;p&gt;It can work over TCP, so we can use netcat on the command line to send messages.&lt;&#x2F;p&gt;
&lt;p&gt;A much better way to understand this protocol is to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.wireshark.org&#x2F;SMPP&quot;&gt;Wireshark&#x27;s SMPP protocol support&lt;&#x2F;a&gt; but for this article, we will do it the hard way.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up&quot;&gt;Setting up&lt;&#x2F;h2&gt;
&lt;p&gt;[Note: the netcat I am using is Ncat 7.70 on Linux.]&lt;&#x2F;p&gt;
&lt;p&gt;The server that receives messages is called an SMSC. You may have your own one, but if not, you can use the CloudHopper one like this:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt install make maven  # (or similar on non-Debian-derived distros)
git clone https:&#x2F;&#x2F;github.com&#x2F;fizzed&#x2F;cloudhopper-smpp.git
cd cloudhopper-smpp&lt;&#x2F;pre&gt;
&lt;p&gt;If you are a little slow, like me, I&#x27;d suggest making it wait a bit longer for bind requests before giving up on you. To do that, edit the main() method of src&#x2F;test&#x2F;java&#x2F;com&#x2F;cloudhopper&#x2F;smpp&#x2F;demo&#x2F;ServerMain.java to add a line like this: configuration.setBindTimeout(500000); on about line 80, near the other similar lines. This will make it wait 500 seconds for you to send a BIND_TRANSCEIVER, instead of giving up after just 5 seconds.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;ve made that change, you can run:&lt;&#x2F;p&gt;
&lt;pre&gt;make server&lt;&#x2F;pre&gt;
&lt;p&gt;Now you have an SMSC running!&lt;&#x2F;p&gt;
&lt;p&gt;Leave that open, and go into another terminal, and type:&lt;&#x2F;p&gt;
&lt;pre&gt;mkfifo tmpfifo
nc 0.0.0.0 2776 &amp;lt; tmpfifo | xxd&lt;&#x2F;pre&gt;
&lt;p&gt;The mkfifp part creates a &quot;fifo&quot; - a named pipe through which we will send our SMPP commands.&lt;&#x2F;p&gt;
&lt;p&gt;The nc part starts Ncat, connecting to the SMSC we started.&lt;&#x2F;p&gt;
&lt;p&gt;The xxd part will take any binary data coming out of Ncat and display it in a more human-readable way.&lt;&#x2F;p&gt;
&lt;p&gt;Leave that open too, and in a third terminal type:&lt;&#x2F;p&gt;
&lt;pre&gt;exec 3&amp;gt; tmpfifo&lt;&#x2F;pre&gt;
&lt;p&gt;This makes everything we send to file descriptor 3 go into the fifo, and therefore into Ncat.&lt;&#x2F;p&gt;
&lt;p&gt;Now we have a way of sending binary data to Ncat, which will send it on to the SMSC and print out any responses.&lt;&#x2F;p&gt;
&lt;p&gt;Note: we will be using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smpp.org&#x2F;SMPP_v3_4_Issue1_2.pdf&quot;&gt;SMPP version 3.4&lt;&#x2F;a&gt; since it is in the widest use, even though it is not the newest.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;terminology&quot;&gt;Terminology&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;SMPP&quot; is the protocol we are speaking, which we are using over TCP&#x2F;IP.&lt;&#x2F;p&gt;
&lt;p&gt;An SMSC is a server (which receives messages intended for phones and sends back responses and receipts).&lt;&#x2F;p&gt;
&lt;p&gt;We will be acting as an ESME or client (which sends messages intended for phones and receives responses and receipts).&lt;&#x2F;p&gt;
&lt;p&gt;The units of information that are passed back and forth in SMPP are called &quot;PDUs&quot; (Protocol Data Units) - these are just bits of binary data that flow over the TCP connection between two computers.&lt;&#x2F;p&gt;
&lt;p&gt;The spec talks about &quot;octets&quot; - this means 8-bit bytes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enquire-link&quot;&gt;ENQUIRE_LINK&lt;&#x2F;h2&gt;
&lt;p&gt;First, we&#x27;ll check the SMSC is responding, by sending an ENQUIRE_LINK, which is used to ask the SMSC whether it&#x27;s there and working.&lt;&#x2F;p&gt;
&lt;p&gt;Go back to the third terminal (where we ran exec) and type this:&lt;&#x2F;p&gt;
&lt;pre&gt;LEN16=&#x27;\x00\x00\x00\x10&#x27;
ENQUIRE_LINK=&#x27;\x00\x00\x00\x15&#x27;
NULL=&#x27;\x00\x00\x00\x00&#x27;
SEQ1=&#x27;\x00\x00\x00\x01&#x27;

echo -n -e &quot;${LEN16}${ENQUIRE_LINK}${NULL}${SEQ1}&quot; &amp;gt;&amp;amp;3&lt;&#x2F;pre&gt;
&lt;p&gt;Explanation: an ENQUIRE_LINK PDU consists of:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes to say the length of the whole PDU in bytes. ENQUIRE_LINK PDUs are always 16 bytes, &quot;00000010&quot; in hex. I called this LEN16.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes to say what type of PDU this is. ENQUIRE_LINK is &quot;00000015&quot; in hex. I called this ENQUIRE_LINK.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes that are always zero in ENQUIRE_LINK. I called this NULL.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes that identify this request, called a sequence number. The response from the server will include this so we can match responses to requests. I called this SEQ1.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Check back in the second terminal (where you ran nc). If everything worked, you should see something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;00000000: 0000 0010 8000 0015 0000 0000 0000 0001  ................&lt;&#x2F;pre&gt;
&lt;p&gt;Ignoring the first and last parts (which are how xxd formats its output), the response we receive is four 4-byte parts, very similar to what we sent:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;4 bytes to say the length of the whole PDU in bytes. Here it is &quot;00000010&quot; hex, or 16 decimal.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes to say what type of PDU this is. Here it is &quot;80000015&quot; in hex, which is the code for ENQUIRE_LINK_RESP.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes for the success status of the ENQUIRE_LINK_RESP. This is always &quot;00000000&quot;, which means success and is called ESME_ROK in the spec.&lt;&#x2F;li&gt;
&lt;li&gt;4 bytes that match the sequence number we sent. This is &quot;00000001&quot;, as we expected.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;bind-transceiver&quot;&gt;BIND_TRANSCEIVER&lt;&#x2F;h2&gt;
&lt;p&gt;Now we can see that the SMSC is working, let&#x27;s &quot;bind&quot; to it. That means something like logging in: we convince the SMSC that we are a legitimate client, and tell it what type of connection we want, and, assuming it agrees, it will hold the connection open for us for as long as we need.&lt;&#x2F;p&gt;
&lt;p&gt;We are going to bind as a transceiver, which means both a transmitter and receiver, so we can both send messages and receive responses.&lt;&#x2F;p&gt;
&lt;p&gt;Send the bind request like this:&lt;&#x2F;p&gt;
&lt;pre&gt;LEN32=&#x27;\x00\x00\x00\x20&#x27;
BIND_TRANSCEIVER=&#x27;\x00\x00\x00\x09&#x27;
NULL=&#x27;\x00\x00\x00\x00&#x27;
SEQ2=&#x27;\x00\x00\x00\x02&#x27;
SYSTEM_ID=&quot;sys\x00&quot;
PASSWORD=&quot;pas\x00&quot;
SYSTEM_TYPE=&#x27;typ\x00&#x27;
INTERFACE_VERSION=&#x27;\x34&#x27;
ADDR_TON=&#x27;\x00&#x27;
ADDR_NPI=&#x27;\x00&#x27;
ADDRESS_RANGE=&#x27;\x00&#x27;

echo -n -e &quot;${LEN32}${BIND_TRANSCEIVER}${NULL}${SEQ2}${SYSTEM_ID}${PASSWORD}${SYSTEM_TYPE}${INTERFACE_VERSION}${ADDR_TON}${ADDR_NPI}${ADDRESS_RANGE}&quot; &amp;gt;&amp;amp;3&lt;&#x2F;pre&gt;
&lt;p&gt;Explanation: this PDU is 32 bytes long, so the first thing we send is &quot;00000020&quot; hex, which is 32.&lt;&#x2F;p&gt;
&lt;p&gt;Then we send &quot;00000009&quot; for the type: BIND_TRANSCEIVER, 4 bytes of zeros, and a sequence number - this time I used 2.&lt;&#x2F;p&gt;
&lt;p&gt;That was the header. Now the body of the PDU starts with a system id (basically a username), a password, and a system type (extra info about who you are). These are all variable-length null-terminated strings, so I ended each one with \x00.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the body is some options about the types of phone number we will be sending from and sending to - I made them all &quot;00&quot; hex, which means &quot;we don&#x27;t know&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If it worked, you should see this in the nc output:&lt;&#x2F;p&gt;
&lt;pre&gt;00000000: 0000 0021 8000 0009 0000 0000 0000 0002  ...!............
00000010: 636c 6f75 6468 6f70 7065 7200 0210 0001  cloudhopper.....&lt;&#x2F;pre&gt;
&lt;p&gt;As before, the first 4 bytes are for how long the PDU is - 33 bytes - and the next 4 bytes are for what type of PDU this is - &quot;80000009&quot; is for BIND_TRANSCEIVER_RESP which is the response to a BIND_TRANSCEIVER.&lt;&#x2F;p&gt;
&lt;p&gt;The next 4 bytes are for the status - these are zeroes which indicates success (ESME_ROK) again. After that is our sequence number (2).&lt;&#x2F;p&gt;
&lt;p&gt;The next 15 bytes are the characters of the word &quot;cloudhopper&quot; followed by a zero - this is the system id of the SMSC.&lt;&#x2F;p&gt;
&lt;p&gt;The next byte (&quot;01&quot;) - the last one we can see - is the beginning of a &quot;TLV&quot;, or optional part of the response. The xxd program actually delayed the last byte of the output, so we can&#x27;t see it yet, but it is &quot;34&quot;. Together, &quot;0134&quot; means &quot;the interface version we support is SMPP 3.4&quot;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;submit-sm&quot;&gt;SUBMIT_SM&lt;&#x2F;h2&gt;
&lt;p&gt;The reason why we&#x27;re here is to send a message. To do that, we use a SUBMIT_SM:&lt;&#x2F;p&gt;
&lt;pre&gt;LEN61=&#x27;\x00\x00\x00\x3d&#x27;
SUBMIT_SM=&#x27;\x00\x00\x00\x04&#x27;
SEQ3=&#x27;\x00\x00\x00\x03&#x27;
SERVICE_TYPE=&#x27;\x00&#x27;
SOURCE_ADDR_TON=&#x27;\x00&#x27;
SOURCE_ADDR_NPI=&#x27;\x00&#x27;
SOURCE_ADDR=&#x27;447000123123\x00&#x27;
DEST_ADDR_TON=&#x27;\x00&#x27;
DEST_ADDR_NPI=&#x27;\x00&#x27;
DESTINATION_ADDR=&#x27;447111222222\x00&#x27;
ESM_CLASS=&#x27;\x00&#x27;
PROTOCOL_ID=&#x27;\x01&#x27;
PRIORITY_FLAG=&#x27;\x01&#x27;
SCHEDULE_DELIVERY_TIME=&#x27;\x00&#x27;
VALIDITY_PERIOD=&#x27;\x00&#x27;
REGISTERED_DELIVERY=&#x27;\x01&#x27;
REPLACE_IF_PRESENT_FLAG=&#x27;\x00&#x27;
DATA_CODING=&#x27;\x03&#x27;
SM_DEFAULT_MSG_ID=&#x27;\x00&#x27;
SM_LENGTH=&#x27;\x04&#x27;
SHORT_MESSAGE=&#x27;hihi&#x27;
echo -n -e &quot;${LEN61}${SUBMIT_SM}${NULL}${SEQ3}${SERVICE_TYPE}${SOURCE_ADDR_TON}${SOURCE_ADDR_NPI}${SOURCE_ADDR}${DEST_ADDR_TON}${DEST_ADDR_NPI}${DESTINATION_ADDR}${ESM_CLASS}${PROTOCOL_ID}${PRIORITY_FLAG}${SCHEDULE_DELIVERY_TIME}${VALIDITY_PERIOD}${REGISTERED_DELIVERY}${REPLACE_IF_PRESENT_FLAG}${DATA_CODING}${SM_DEFAULT_MSG_ID}${SM_LENGTH}${SHORT_MESSAGE}&quot; &amp;gt;&amp;amp;3

&lt;&#x2F;pre&gt;
&lt;p&gt;LEN61 is the length in bytes of the PDU, SUBMIT_SM is the type of PDU, and SEQ3 is a sequence number, as before.&lt;&#x2F;p&gt;
&lt;p&gt;SOURCE_ADDR is a null-terminated (i.e. it ends with a zero byte) string of ASCII characters saying who the message is from. This can be a phone number, or a name (but the rules about what names are allowed are complicated and region-specific). SOURCE_ADDR_TON and SOURCE_ADDR_NPI give information about what type of address we are providing - we set them to zero to mean &quot;we don&#x27;t know&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;DESTINATION_ADDR, DEST_ADDR_TON and DEST_ADDR_NPI describe the phone number we are sending to.&lt;&#x2F;p&gt;
&lt;p&gt;ESM_CLASS tells the SMSC how to treat your message - we use &quot;store and forward&quot; mode, which means keep it and send it when you can.&lt;&#x2F;p&gt;
&lt;p&gt;PROTOCOL_ID is different depending what type of SMSC you are using. We assume GSM here, and provide a value that works for GSM.&lt;&#x2F;p&gt;
&lt;p&gt;PRIORITY_FLAG means how important the message is - we used &quot;interactive&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;SCHEDULE_DELIVERY_TIME is when to send - we say &quot;immediate&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;VALIDITY_PERIOD means how long should this message live before we give up trying to send it (e.g. if the user&#x27;s phone is off). We use &quot;default&quot; so the SMSC will do something sensible.&lt;&#x2F;p&gt;
&lt;p&gt;REGISTERED_DELIVERY gives information about whether we want a receipt saying the message arrived on the phone. We say &quot;yes please&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;REPLACE_IF_PRESENT_FLAG tells it what to do if a duplicate of this message is sent to the SMSC before this one is delivered - the value we used means &quot;don&#x27;t replace&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;DATA_CODING states what &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2018&#x2F;11&#x2F;24&#x2F;interesting-characters-utf-16-utf-8-unicode-encodings-video&#x2F;&quot;&gt;character encoding&lt;&#x2F;a&gt; you are using to send the message text - we used &quot;Latin 1&quot;, which means ISO-8859-1.&lt;&#x2F;p&gt;
&lt;p&gt;SM_DEFAULT_MSG_ID allows us to use one of a handful of hard-coded standard messages - we say &quot;no, use a custom one&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;SM_LENGTH is the length in bytes of the &quot;short message&quot; - the actual text that the user will see on the phone screen.&lt;&#x2F;p&gt;
&lt;p&gt;SHORT_MESSAGE is the short message itself - our message is all ASCII characters, but we could use any bytes and they will be interpreted as characters in ISO-8859-1 encoding.&lt;&#x2F;p&gt;
&lt;p&gt;You should see a response in the other terminal like this:&lt;&#x2F;p&gt;
&lt;pre&gt;00000020: 3400 0000 1180 0000 0400 0000 0000 0000  4...............&lt;&#x2F;pre&gt;
&lt;p&gt;The initial &quot;34&quot; is the left-over byte from the previous message as mentioned above. After that, we have:&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000011&quot; for the length of this PDU (17 bytes).&lt;&#x2F;p&gt;
&lt;p&gt;&quot;80000004&quot; for the type - SUBMIT_SM_RESP which tells us whether the message was accepted (but not whether it was received).&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000000&quot; for the status - zero means &quot;OK&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The last two bytes are chopped off again, but what we actually get back is:&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000003&quot;, which is the sequence number, and then:&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00&quot; which is a null-terminated ASCII message ID: in this case the SMSC is saying that the ID it has given this message is &quot;&quot;, which is probably not very helpful! If this ID were not empty, it would help us later if we receive a delivery receipt, or if we want to ask about the message, or change or cancel it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;deliver-sm&quot;&gt;DELIVER_SM&lt;&#x2F;h2&gt;
&lt;p&gt;If you stop the SMSC process (the one we started with make server) by pressing Ctrl-C, and start a different one with make server-echo, and then repeat the other commands (note you need to be quick because you only get 5 seconds to bind before it gives up on you - make similar changes to what we did in ServerMain to ServerEchoMain if this causes problems), you will receive a delivery receipt from the server, which looks like this:&lt;&#x2F;p&gt;
&lt;p&gt;&quot;0000003d&quot; for the length of this PDU (59 bytes).&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000005&quot; for the type (DELIVER_SM).&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000000&quot; for the unused command status.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00000001&quot; as a sequence number. Note, this is unrelated the sequence number of the original message: to match with the original message, we must use the message ID provided in the SUBMIT_SM_RESP.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;0000003400&quot; to mean we are using SMPP 3.4. (This is a null-terminated string of bytes.)&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00&quot; and &quot;00&quot; for the TON and NPI of the source address, followed by the source address itself, which is a null-terminated ASCII string: &quot;34343731313132323232323200&quot;. This translates to &quot;447111222222&quot;, which was the destination address of our original message. Note: some SMSCs switch the source and destination addresses like this in their delivery receipts, and some don&#x27;t, which makes life interesting.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;00&quot; and &quot;00&quot; for the TON and NPI of the destination address, followed by &quot;34343730303031323331323300&quot; for the address itself, which translates to &quot;447000123123&quot;, as expected.&lt;&#x2F;p&gt;
&lt;p&gt;The DELIVER_SM PDU continues with much of the information repeated from the original message, and the SMSC is allowed to provide a short message as part of the receipt - in our example the cloudhopper SMSC repeats the original message. Some SMSCs use the short message to provide information such as the message ID and the delivery time, but there is no formal standard for how to provide it. Other SMSCs use a TLV to provide the message ID instead.&lt;&#x2F;p&gt;
&lt;p&gt;Somewhere in the DELIVER_SM you should find some indication of whether the message was actually delivered to the phone. Often it&#x27;s in a TLV called &quot;message state&quot;, but it could also be in the message body. Bizarrely, a state of &quot;4&quot; is the normal code for &quot;delivered successfully&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;In order to complete the conversation, you should provide a DELIVER_SM_RESP, and then an UNBIND, but hopefully based on what we&#x27;ve done and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;smpp.org&#x2F;SMPP_v3_4_Issue1_2.pdf&quot;&gt;SMPP 3.4 standard&lt;&#x2F;a&gt;, you should be able to figure it out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-did-it&quot;&gt;You did it&lt;&#x2F;h2&gt;
&lt;p&gt;SMPP is a binary protocol layered directly on top of TCP, which makes it slightly harder to work with by hand than the HTTP protocols with which many of us are more familiar, but I hope I&#x27;ve convinced you it&#x27;s possible to understand what&#x27;s going on without resorting to some kind of heavyweight debugging tool or library.&lt;&#x2F;p&gt;
&lt;p&gt;Happy texting!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP on racism in the US and UK</title>
          <pubDate>Fri, 05 Jun 2020 12:26:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/06/05/letter-to-my-mp-on-racism-in-the-us-and-uk/</link>
          <guid>https://artificialworlds.net/blog/2020/06/05/letter-to-my-mp-on-racism-in-the-us-and-uk/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/06/05/letter-to-my-mp-on-racism-in-the-us-and-uk/">&lt;p&gt;Here is the text of a letter I wrote to my MP today via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&quot;&gt;writetothem.com&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Dear [], I am writing to you to express my growing horror at the ongoing police violence and brutality Black protesters are facing in the US, and the UK&#x27;s disturbing silence on the matter. These protests, begun as peaceful demonstrations against the state-enabled murder of George Floyd at the hands of the Minneapolis Police Department, have been escalated by the US police, who are using excessive force against protesters practising their civic rights. If this were a non-white country, Western countries would have denounced this brutality; instead, the UK has elected to remain silent. I am painfully aware that the UK itself suffers from many racist structures. Thanks to systematic injustice, the recent pandemic has impacted BAME people at a vastly higher rate than white people. The death of Belly Mujinga, caused by a racist attack and by her presence at work despite underlying conditions that should have resulted in paid leave, was horrifying and preventable, and the dropped inquiry utterly disgraceful. I ask you personally to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;write to the Foreign Secretary asking for a strong statement of condemnation of police brutality in the US,&lt;&#x2F;li&gt;
&lt;li&gt;make it a personal priority to address the systematic injustice of unfair outcomes for BAME people in the UK,&lt;&#x2F;li&gt;
&lt;li&gt;ensure that the government responds materially to the report on BAME COVID-19 deaths,&lt;&#x2F;li&gt;
&lt;li&gt;insist on a full enquiry into the circumstances surrounding Belly Mujinga&#x27;s death. Yours sincerely,
[]&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Inspired by: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;anahitrooz&#x2F;status&#x2F;1268600449453772800&quot;&gt;@anahitrooz&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Example Android project with repeatable tests running inside an emulator</title>
          <pubDate>Wed, 06 May 2020 11:33:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/05/06/example-android-project-with-repeatable-tests-running-inside-an-emulator/</link>
          <guid>https://artificialworlds.net/blog/2020/05/06/example-android-project-with-repeatable-tests-running-inside-an-emulator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/05/06/example-android-project-with-repeatable-tests-running-inside-an-emulator/">&lt;p&gt;I&#x27;ve spent the last couple of days fighting the Android command line to set up a simple project that can run automated tests inside an emulator reliably and repeatably.&lt;&#x2F;p&gt;
&lt;p&gt;To make the tests reliable and independent from anything else on my machine, I wanted to store the Android SDK and AVD files in a local directory.&lt;&#x2F;p&gt;
&lt;p&gt;To do this I had to define a lot of inter-related environment variables, and wrap the tools in scripts that ensure they run with the right flags and settings.&lt;&#x2F;p&gt;
&lt;p&gt;The end result of this work is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;android-skeleton&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;android-skeleton&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You need all the utility scripts included in that repo for it to work, but some highlights include:&lt;&#x2F;p&gt;
&lt;p&gt;The environment variables that I source in every script, scripts&#x2F;paths:&lt;pre&gt;PROJECT_ROOT=$(dirname $(dirname $(realpath ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]})))
export ANDROID_SDK_ROOT=&quot;${PROJECT_ROOT}&#x2F;android_sdk&quot;
export ANDROID_SDK_HOME=&quot;${ANDROID_SDK_ROOT}&quot;
export ANDROID_EMULATOR_HOME=&quot;${ANDROID_SDK_ROOT}&#x2F;emulator-home&quot;
export ANDROID_AVD_HOME=&quot;${ANDROID_EMULATOR_HOME}&#x2F;avd&quot;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Creation of a local.properties file that tells Gradle and Android Studio where the SDK is, by running something like this:&lt;pre&gt;echo &quot;# File created automatically - changes will be overwritten!&quot; &amp;gt; local.properties
echo &quot;sdk.dir=${ANDROID_SDK_ROOT}&quot; &amp;gt;&amp;gt; local.properties&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The wrapper scripts for Android tools e.g. scripts&#x2F;sdkmanager:&lt;pre&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;p&gt;set -e
set -u&lt;&#x2F;p&gt;
&lt;p&gt;source scripts&#x2F;paths&lt;&#x2F;p&gt;
&lt;p&gt;&quot;${ANDROID_SDK_ROOT}&#x2F;tools&#x2F;bin&#x2F;sdkmanager&quot; &lt;br &#x2F;&gt;
&quot;--sdk_root=${ANDROID_SDK_ROOT}&quot; &lt;br &#x2F;&gt;
&quot;$@&quot;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The wrapper for avdmanager is particularly interesting since it seems we need to override where it thinks the tools directory is for it to work properly - scripts&#x2F;avdmanager:&lt;pre&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;p&gt;set -e
set -u&lt;&#x2F;p&gt;
&lt;p&gt;source scripts&#x2F;paths&lt;&#x2F;p&gt;
&lt;h1 id=&quot;set-toolsdir-to-include-bin-since-avdmanager-seems-to-go-2-dirs-up&quot;&gt;Set toolsdir to include &quot;bin&#x2F;&quot; since avdmanager seems to go 2 dirs up&lt;&#x2F;h1&gt;
&lt;h1 id=&quot;from-that-to-find-the-sdk-root&quot;&gt;from that to find the SDK root?&lt;&#x2F;h1&gt;
&lt;p&gt;AVDMANAGER_OPTS=&quot;-Dcom.android.sdkmanager.toolsdir=${ANDROID_SDK_ROOT}&#x2F;tools&#x2F;bin&#x2F;&quot; &lt;br &#x2F;&gt;
&quot;${ANDROID_SDK_ROOT}&#x2F;tools&#x2F;bin&#x2F;avdmanager&quot; &quot;$@&quot;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;An installation script that must be run once before using the project scripts&#x2F;install-android-tools:&lt;pre&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;p&gt;set -e
set -u
set -x&lt;&#x2F;p&gt;
&lt;p&gt;source scripts&#x2F;paths&lt;&#x2F;p&gt;
&lt;p&gt;mkdir -p &quot;${ANDROID_SDK_ROOT}&quot;
mkdir -p &quot;${ANDROID_AVD_HOME}&quot;
mkdir -p &quot;${ANDROID_EMULATOR_HOME}&quot;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;download-sdkmanager-avdmanager-etc&quot;&gt;Download sdkmanager, avdmanager etc.&lt;&#x2F;h1&gt;
&lt;p&gt;cd &quot;${ANDROID_SDK_ROOT}&quot;
test -f commandlinetools-&lt;em&gt;.zip || &lt;br &#x2F;&gt;
wget -q &#x27;https:&#x2F;&#x2F;dl.google.com&#x2F;android&#x2F;repository&#x2F;commandlinetools-linux-6200805_latest.zip&#x27;
unzip -q -u commandlinetools-&lt;&#x2F;em&gt;.zip
cd ..&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ask-sdkmanager-to-update-itself&quot;&gt;Ask sdkmanager to update itself&lt;&#x2F;h1&gt;
&lt;p&gt;.&#x2F;scripts&#x2F;sdkmanager --update&lt;&#x2F;p&gt;
&lt;h1 id=&quot;install-the-emulator-and-tools&quot;&gt;Install the emulator and tools&lt;&#x2F;h1&gt;
&lt;p&gt;yes | .&#x2F;scripts&#x2F;sdkmanager --install &#x27;emulator&#x27; &#x27;platform-tools&#x27;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;platforms&quot;&gt;Platforms&lt;&#x2F;h1&gt;
&lt;p&gt;.&#x2F;scripts&#x2F;sdkmanager --install &#x27;platforms;android-21&#x27;
.&#x2F;scripts&#x2F;sdkmanager --install &#x27;platforms;android-29&#x27;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;install-system-images-for-our-oldest-and-newest-supported-api-versions&quot;&gt;Install system images for our oldest and newest supported API versions&lt;&#x2F;h1&gt;
&lt;p&gt;yes | .&#x2F;scripts&#x2F;sdkmanager --install &#x27;system-images;android-21;default;x86_64&#x27;
yes | .&#x2F;scripts&#x2F;sdkmanager --install &#x27;system-images;android-29;default;x86_64&#x27;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;create-avds-to-run-the-system-images&quot;&gt;Create AVDs to run the system images&lt;&#x2F;h1&gt;
&lt;p&gt;echo no | .&#x2F;scripts&#x2F;avdmanager -v &lt;br &#x2F;&gt;
create avd &lt;br &#x2F;&gt;
-f &lt;br &#x2F;&gt;
-n &quot;avd-21&quot; &lt;br &#x2F;&gt;
-k &quot;system-images;android-21;default;x86_64&quot; &lt;br &#x2F;&gt;
-p ${ANDROID_SDK_ROOT}&#x2F;avds&#x2F;avd-21
echo no | .&#x2F;scripts&#x2F;avdmanager -v &lt;br &#x2F;&gt;
create avd &lt;br &#x2F;&gt;
-f &lt;br &#x2F;&gt;
-n &quot;avd-29&quot; &lt;br &#x2F;&gt;
-k &quot;system-images;android-29;default;x86_64&quot; &lt;br &#x2F;&gt;
-p ${ANDROID_SDK_ROOT}&#x2F;avds&#x2F;avd-29&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Please do contribute to the project if you know easier ways to do this stuff.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Creating a tiny Docker image of a Rust project</title>
          <pubDate>Wed, 22 Apr 2020 11:40:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/04/22/creating-a-tiny-docker-image-of-a-rust-project/</link>
          <guid>https://artificialworlds.net/blog/2020/04/22/creating-a-tiny-docker-image-of-a-rust-project/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/04/22/creating-a-tiny-docker-image-of-a-rust-project/">&lt;p&gt;I am building a toy project in Rust to help me learn how to deploy things in AWS. I&#x27;m considering using Elastic Beanstalk (AWS&#x27;s platform-as-a-service) and also Kubernetes. Both of these support deploying via Docker containers, so I am learning how to package a Rust executable as a Docker image.&lt;&#x2F;p&gt;
&lt;p&gt;My program is a small web site that uses Redis as a back end database. It consists of some Rust code and a couple of static files.&lt;&#x2F;p&gt;
&lt;p&gt;Because Rust has good support for building executables with very few dependencies, we can actually build a Docker image with almost nothing in it, except my program and the static files.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to Alexander Brand&#x27;s blog post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alexbrand.dev&#x2F;post&#x2F;how-to-package-rust-applications-into-minimal-docker-containers&#x2F;&quot;&gt;How to Package Rust Applications Into Minimal Docker Containers&lt;&#x2F;a&gt; I was able to build a Docker image that:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Is very small&lt;&#x2F;li&gt;
&lt;li&gt;Does not take too long to build&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The main concern for making the build faster is that we don&#x27;t download and build all the dependencies every time. To achieve that we make sure there is a layer in the Docker build process that includes all the dependencies being built, and is not re-built when we only change our source code.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the Dockerfile I ended up with:&lt;&#x2F;p&gt;
&lt;pre&gt;# 1: Build the exe
FROM rust:1.42 as builder
WORKDIR &#x2F;usr&#x2F;src

# 1a: Prepare for static linking
RUN apt-get update &amp;amp;&amp;amp; \
    apt-get dist-upgrade -y &amp;amp;&amp;amp; \
    apt-get install -y musl-tools &amp;amp;&amp;amp; \
    rustup target add x86_64-unknown-linux-musl

# 1b: Download and compile Rust dependencies (and store as a separate Docker layer)
RUN USER=root cargo new myprogram
WORKDIR &#x2F;usr&#x2F;src&#x2F;myprogram
COPY Cargo.toml Cargo.lock .&#x2F;
RUN cargo install --target x86_64-unknown-linux-musl --path .

# 1c: Build the exe using the actual source code
COPY src .&#x2F;src
RUN cargo install --target x86_64-unknown-linux-musl --path .

# 2: Copy the exe and extra files (&quot;static&quot;) to an empty Docker image
FROM scratch
COPY --from=builder &#x2F;usr&#x2F;local&#x2F;cargo&#x2F;bin&#x2F;myprogram .
COPY static .
USER 1000
CMD [&quot;.&#x2F;myprogram&quot;]&lt;&#x2F;pre&gt;
&lt;p&gt;The FROM rust:1.42 as build line uses the newish Docker feature &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;develop&#x2F;develop-images&#x2F;multistage-build&#x2F;&quot;&gt;multi-stage builds&lt;&#x2F;a&gt; - we create one Docker image (&quot;builder&quot;) just to build the code, and then copy the resulting executable into the final Docker image.&lt;&#x2F;p&gt;
&lt;p&gt;In order to allow us to build a stand-alone executable that does not depend on the standard libraries in the operating system, we use the &quot;musl&quot; target, which is designed to be statically linked.&lt;&#x2F;p&gt;
&lt;p&gt;The final Docker image produced is pretty much the same size as the release build of myprogram, and the build is fast, so long as I don&#x27;t change the dependencies in Cargo.toml.&lt;&#x2F;p&gt;
&lt;p&gt;A couple more tips to make the build faster:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Use a .dockerignore file. Here is mine:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre&gt;&#x2F;target&#x2F;
&#x2F;.git&#x2F;&lt;&#x2F;pre&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;develop&#x2F;develop-images&#x2F;build_enhancements&#x2F;&quot;&gt;Docker BuildKit&lt;&#x2F;a&gt;, by running the build like this:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;pre&gt;DOCKER_BUILDKIT=1 docker build  .&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Keeping track of podcast times with a simple bash script on Linux</title>
          <pubDate>Sat, 04 Apr 2020 02:29:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/04/04/keeping-track-of-podcast-times-with-a-simple-bash-script-on-linux/</link>
          <guid>https://artificialworlds.net/blog/2020/04/04/keeping-track-of-podcast-times-with-a-simple-bash-script-on-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/04/04/keeping-track-of-podcast-times-with-a-simple-bash-script-on-linux/">&lt;p&gt;I was recording some podcast audio tonight and wanted to be able to press a single key when I reached a significant moment, so I could add the times to the show notes.&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t find anything that already did this, so I wrote a tiny bash script. I ran this script and pressed Enter whenever I wanted a time recorded:&lt;&#x2F;p&gt;
&lt;pre&gt;T=0
echo
while sleep 1; do
    echo -n -e &quot;\e[1A&quot;
    echo $(($T &#x2F; 60))m $(($T % 60))s
    T=$(($T + 1))
done&lt;&#x2F;pre&gt;
&lt;p&gt;The output looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;times.bash
0m 41s
6m 16s
9m 59s
13m 30s&lt;&#x2F;pre&gt;
&lt;p&gt;The time ticks along, and when you press Enter that time stamp is recorded. You can copy this text out of your terminal to use in show notes. On most terminals you can copy text by selecting it with the mouse and pressing Ctrl-Shift-v.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Custom Bash tab completion for my program</title>
          <pubDate>Wed, 11 Mar 2020 17:35:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/03/11/custom-bash-tab-completion-for-my-program/</link>
          <guid>https://artificialworlds.net/blog/2020/03/11/custom-bash-tab-completion-for-my-program/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/03/11/custom-bash-tab-completion-for-my-program/">&lt;p&gt;I love &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;bash&#x2F;&quot;&gt;Bash&lt;&#x2F;a&gt; tab completion, and I want it for the command I am writing, so it can automatically complete parts of the command line when I run my program.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code&quot;&gt;Code&lt;&#x2F;h2&gt;
&lt;p&gt;Here is the script (install-bash-completion) I wrote to set it up (no need to be root - it installs in ~&#x2F;.local):&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -u
set -e

DIR=${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME&#x2F;.local&#x2F;share}&#x2F;bash-completion}&#x2F;completions

mkdir -p ${DIR}

cp _myprogram ${DIR}&lt;&#x2F;pre&gt;
&lt;p&gt;The actual completion script (_myprogram) it installs looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;_myprogram_commands()
{
    local cur prev opts
    COMPREPLY=()
    cur=&quot;${COMP_WORDS[COMP_CWORD]}&quot;
    prev=&quot;${COMP_WORDS[COMP_CWORD-1]}&quot;
    opts=$(bash -c &quot;.&#x2F;myprogram --commands&quot;)

    COMPREPLY=( $(compgen -W &quot;${opts}&quot; -- ${cur}) )
    return 0
}
complete -F _myprogram_commands .&#x2F;myprogram&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;installing&quot;&gt;Installing&lt;&#x2F;h2&gt;
&lt;p&gt;To install it, run:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;install-bash-completion&lt;&#x2F;pre&gt;
&lt;p&gt;Then log out and log in again.&lt;&#x2F;p&gt;
&lt;p&gt;Now when you type .&#x2F;myprogram  and press TAB a couple of times, you should see the possible completions listed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notes&quot;&gt;Notes&lt;&#x2F;h2&gt;
&lt;p&gt;The completion script must be named to match the program name, with a leading underscore.&lt;&#x2F;p&gt;
&lt;p&gt;If I wanted it to work when it was installed in my PATH, I would need to change .&#x2F;myprogram to just myprogram in 2 places.&lt;&#x2F;p&gt;
&lt;p&gt;Notice the line opts=$(bash -c &quot;.&#x2F;myprogram --commands&quot;) - it actually runs my program to get the list of completions. This means my program needs to accept a --commands option which prints the valid commands. Alternatively, I could have hard-coded it by replacing that line with just:&lt;&#x2F;p&gt;
&lt;pre&gt;opts=&quot;cmd1 cmd2 --help --etc&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;More info:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;iridakos.com&#x2F;programming&#x2F;2018&#x2F;03&#x2F;01&#x2F;bash-programmable-completion-tutorial&quot;&gt;Creating a bash completion script&lt;&#x2F;a&gt; by Lazarus Lazaridis&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;debian-administration.org&#x2F;article&#x2F;316&#x2F;An_introduction_to_bash_completion_part_1&quot;&gt;An introduction to bash completion&lt;&#x2F;a&gt; from Debian Administration&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Struggling with Rust to figure out the right types for a function signature</title>
          <pubDate>Wed, 04 Mar 2020 12:08:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/03/04/struggling-with-rust-to-figure-out-the-right-types-for-a-function-signature/</link>
          <guid>https://artificialworlds.net/blog/2020/03/04/struggling-with-rust-to-figure-out-the-right-types-for-a-function-signature/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/03/04/struggling-with-rust-to-figure-out-the-right-types-for-a-function-signature/">&lt;p&gt;I am loving writing code in Rust. So many things about the language and its ecosystem feel so &lt;em&gt;right&lt;&#x2F;em&gt;*.&lt;&#x2F;p&gt;
&lt;p&gt;* For example: ownership of objects, expressive type system, compile to native, offline API docs, immutability, high quality libraries.&lt;&#x2F;p&gt;
&lt;p&gt;One of the things I like about it is that I don&#x27;t feel like I need to use an IDE, so I can happily code in Vim with no clever plugins.&lt;&#x2F;p&gt;
&lt;p&gt;One thing an IDE might give me would be an &quot;extract function&quot; refactoring. In most languages I am happy to do that manually, because I can let the compile errors guide me on what my function should look like.&lt;&#x2F;p&gt;
&lt;p&gt;However, in Rust I sometimes find it&#x27;s hard to find the right signature for a function I want to extract, and I am struggling to persuade the compiler to help me.&lt;&#x2F;p&gt;
&lt;p&gt;Here is an example from my new &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;listsync&quot;&gt;listsync&lt;&#x2F;a&gt; project, in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;listsync&#x2F;listsync-client-rust&#x2F;-&#x2F;blob&#x2F;6c16aadf7671c610c1c197cabfe71a394a2f2979&#x2F;src&#x2F;bin&#x2F;listsync-client-rust.rs&quot;&gt;listsync-client-rust.rs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;use actix_web::{middleware, App, HttpServer};
use listsync_client_rust;
&#x2F;&#x2F; ...
#[actix_rt::main]
async fn main() -&amp;gt; std::io::Result&amp;lt;()&amp;gt; {
&#x2F;&#x2F;...
    HttpServer::new(|| {
        &lt;span style=&quot;color: green;&quot;&gt;App::new()
            .wrap(listsync_client_rust::cookie_session::new_session())
            .wrap(middleware::Logger::default())
            .configure(listsync_client_rust::config)&lt;&#x2F;span&gt;
    })
&#x2F;&#x2F;...
&lt;&#x2F;pre&gt;
&lt;p&gt;I would like to extract the code highlighted above, the creation of an App, into a separate function, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;fn new_app() -&amp;gt; &lt;span style=&quot;color: red;&quot;&gt;???&lt;&#x2F;span&gt; {
    &lt;span style=&quot;color: green;&quot;&gt;App::new()
        .wrap(listsync_client_rust::cookie_session::new_session())
        .wrap(middleware::Logger::default())
        .configure(listsync_client_rust::config)&lt;&#x2F;span&gt;
}
&#x2F;&#x2F;...
    HttpServer::new(|| {
        &lt;span style=&quot;color: green;&quot;&gt;new_app()&lt;&#x2F;span&gt;
    })
&lt;&#x2F;pre&gt;
&lt;p&gt;Simple, right? To find out what the return type of the function should be, I can just make a bad guess, and get the compiler to tell me what I did wrong. In this case, I will guess by changing the question marks above into i32, and run cargo test. I get quite a few errors, one of which is:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: red;&quot;&gt;error[E0277]: the trait bound `i32: actix_service::IntoServiceFactory&amp;lt;_&amp;gt;` is not satisfied&lt;&#x2F;span&gt;
  --&amp;gt; src&#x2F;bin&#x2F;listsync-client-rust.rs:27:5
   |
27 | &#x2F;     HttpServer::new(|| {
28 | |         new_app()
29 | |     })
   | |______^ the trait `actix_service::IntoServiceFactory&amp;lt;_&amp;gt;` is not implemented for `i32`
   |
   = note: required by `actix_web::server::HttpServer`
&lt;&#x2F;pre&gt;
&lt;p&gt;So the first problem I see is that the error message I am seeing is about the later code, and there are no errors about my new function.&lt;&#x2F;p&gt;
&lt;p&gt;I obviously went a little too fast. Let&#x27;s change the HttpServer::new code back to how it was before, and only make a new function new_app. Now I get an error that should help me:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: red;&quot;&gt;error[E0308]: mismatched types&lt;&#x2F;span&gt;
  --&amp;gt; src&#x2F;bin&#x2F;listsync-client-rust.rs:12:5
   |
11 |   fn new_app() -&amp;gt; &lt;span style=&quot;color: red;&quot;&gt;i32&lt;&#x2F;span&gt; {
   |                   --- expected `i32` because of return type
12 | &#x2F;     App::new()
13 | |         .wrap(listsync_client_rust::cookie_session::new_session())
14 | |         .wrap(middleware::Logger::default())
15 | |         .configure(listsync_client_rust::config)
   | |________________________________________________^ expected i32, found struct `actix_web::app::App`
   |
   = note: expected type `i32`
              found type `&lt;span style=&quot;color: green;&quot;&gt;actix_web::app::App&amp;lt;impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog&amp;lt;actix_http::body::Body&amp;gt;&amp;gt;&lt;&#x2F;span&gt;`
&lt;&#x2F;pre&gt;
&lt;p&gt;So the compiler has told us what type we are returning! Let&#x27;s copy that into the type signature of the function:&lt;&#x2F;p&gt;
&lt;pre&gt;use actix_service::ServiceFactory;
use actix_http::body::Body;
&#x2F;&#x2F; ...
fn new_app() -&amp;gt; &lt;span style=&quot;color: green;&quot;&gt;App&amp;lt;impl ServiceFactory, middleware::logger::StreamLog&amp;lt;Body&amp;gt;&amp;gt;&lt;&#x2F;span&gt; {
&#x2F;&#x2F; ...
&lt;&#x2F;pre&gt;
&lt;p&gt;The first error I get from the compiler is a distraction:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: red;&quot;&gt;error[E0432]: unresolved import `actix_service`&lt;&#x2F;span&gt;
 --&amp;gt; src&#x2F;bin&#x2F;listsync-client-rust.rs:1:5
  |
1 | use actix_service::ServiceFactory;
  |     ^^^^^^^^^^^^^ use of undeclared type or module `actix_service`
&lt;&#x2F;pre&gt;
&lt;p&gt;I can fix it by adding actix-service = &quot;1.0.5&quot; to Cargo.toml. (I found the version by looking in Cargo.lock, since this dependency was already implicitly used - I just need to make it explicit if I am going to use it directly.)&lt;&#x2F;p&gt;
&lt;p&gt;Once I do that I get the next error:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: red;&quot;&gt;error[E0603]: module `logger` is private&lt;&#x2F;span&gt;
  --&amp;gt; src&#x2F;bin&#x2F;listsync-client-rust.rs:13:54
   |
13 | fn new_app() -&amp;gt; App&amp;lt;impl ServiceFactory, middleware::logger::StreamLog&amp;lt;Body&amp;gt;&amp;gt; {
   |                                                      ^^^^^^
&lt;&#x2F;pre&gt;
&lt;p&gt;This leaves me a bit stuck: I can&#x27;t use StreamLog because it&#x27;s in a private module.&lt;&#x2F;p&gt;
&lt;p&gt;More importantly, it makes the point that I don&#x27;t actually want to be as specific as I am being: I don&#x27;t care what the exact type parameters for App are - I just want to return an App of some kind and have the compiler fill in the blanks. Ideally, if I change the body of new_app later, for example to add another wrap call that changes the type of App we are returning, I&#x27;d like to leave the return type the same and have it just work.&lt;&#x2F;p&gt;
&lt;p&gt;With that in mind, I took a look at the type that HttpServer::new takes in. Here is impl HttpServer:&lt;&#x2F;p&gt;
&lt;pre&gt;impl&amp;lt;F, I, S, B&amp;gt; HttpServer&amp;lt;F, I, S, B&amp;gt; where
    F: Fn() -&amp;gt; I + Send + Clone + &#x27;static,
    I: IntoServiceFactory&amp;lt;S&amp;gt;,
    S: ServiceFactory&amp;lt;Config = AppConfig, Request = Request&amp;gt;,
    S::Error: Into&amp;lt;Error&amp;gt; + &#x27;static,
    S::InitError: Debug,
    S::Response: Into&amp;lt;Response&amp;lt;B&amp;gt;&amp;gt; + &#x27;static,
    &amp;lt;S::Service as Service&amp;gt;::Future: &#x27;static,
    B: MessageBody + &#x27;static,
&lt;&#x2F;pre&gt;
&lt;p&gt;and HttpServer::new looks like:&lt;&#x2F;p&gt;
&lt;pre&gt;pub fn new(factory: F) -&amp;gt; Self&lt;&#x2F;pre&gt;
&lt;p&gt;So it takes in a function which actually makes the App, and the type of that function is F, which is a Fn which returns a I + Send + Clone + &#x27;static. From the declaration of impl HttpServer we can see that the type of I depends on S and B, which have quite complex types. Let&#x27;s paste the whole thing in:&lt;&#x2F;p&gt;
&lt;pre&gt;use actix_http::{Error, Request, Response};
use actix_service::IntoServiceFactory;
use actix_web::body::MessageBody;
use actix_web::dev::{AppConfig, Service};
use core::fmt::Debug;
&#x2F;&#x2F; ...
fn new_app&amp;lt;I, S, B&amp;gt;() -&amp;gt; I
where
    I: IntoServiceFactory&amp;lt;S&amp;gt; + Send + Clone + &#x27;static,
    S: ServiceFactory&amp;lt;Config = AppConfig, Request = Request&amp;gt;,
    S::Error: Into&amp;lt;Error&amp;gt; + &#x27;static,
    S::InitError: Debug,
    S::Response: Into&amp;lt;Response&amp;lt;B&amp;gt;&amp;gt; + &#x27;static,
    &amp;lt;S::Service as Service&amp;gt;::Future: &#x27;static,
    B: MessageBody + &#x27;static,
{
    App::new()
        .wrap(listsync_client_rust::cookie_session::new_session())
        .wrap(middleware::Logger::default())
        .configure(listsync_client_rust::config)
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Note that I had to modify I to include the extra requirements on the return type of F from the definition of HttpServer. (I think I did the right thing, but I&#x27;m not sure. If I just remove the  + Send + Clone + &#x27;static it seems to behave similarly.)&lt;&#x2F;p&gt;
&lt;p&gt;Now I get this error from the compiler:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: red;&quot;&gt;error[E0308]: mismatched types&lt;&#x2F;span&gt;
  --&amp;gt; src&#x2F;bin&#x2F;listsync-client-rust.rs:27:5
   |
17 |   fn new_app&amp;lt;I, S, B&amp;gt;() -&amp;gt; I
   |                            - expected `I` because of return type
...
27 | &#x2F;     App::new()
28 | |         .wrap(listsync_client_rust::cookie_session::new_session())
29 | |         .wrap(middleware::Logger::default())
30 | |         .configure(listsync_client_rust::config)
   | |________________________________________________^ expected type parameter, found struct `actix_web::app::App`
   |
   = note: expected type `I`
              found type `actix_web::app::App&amp;lt;impl actix_service::ServiceFactory, actix_web::middleware::logger::StreamLog&amp;lt;actix_http::body::Body&amp;gt;&amp;gt;`
   = help: type parameters must be constrained to match other types
   = note: for more information, visit https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;book&#x2F;ch10-02-traits.html#traits-as-parameters
&lt;&#x2F;pre&gt;
&lt;p&gt;The compiler really tries to help here, suggesting I read a chapter of the Rust Book, but even after reading it I could not figure out how to do what I am trying to do.&lt;&#x2F;p&gt;
&lt;p&gt;Can anyone help me?&lt;&#x2F;p&gt;
&lt;p&gt;Wouldn&#x27;t it be amazing if there were some way the compiler could give me easier-to-understand help to figure this out?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>React Concepts Video</title>
          <pubDate>Thu, 13 Feb 2020 22:19:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/02/13/react-concepts-video/</link>
          <guid>https://artificialworlds.net/blog/2020/02/13/react-concepts-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/02/13/react-concepts-video/">&lt;p&gt;I tried to describe the main ideas that will help you write decent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;&quot;&gt;React&lt;&#x2F;a&gt; code. Maybe it will help you understand the docs a bit more when you read them...&lt;&#x2F;p&gt;
&lt;iframe title=&quot;React Concepts&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c920374d-78cd-4083-a0a8-c1c436d4712c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;react-concepts&#x2F;react-concepts.html&quot;&gt;React Concepts slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Converting HTML slides to a PDF with Firefox</title>
          <pubDate>Fri, 24 Jan 2020 16:46:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/01/24/converting-html-slides-to-a-pdf-with-firefox/</link>
          <guid>https://artificialworlds.net/blog/2020/01/24/converting-html-slides-to-a-pdf-with-firefox/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/01/24/converting-html-slides-to-a-pdf-with-firefox/">&lt;p&gt;I have not found an automated way to generate a nice PDF from some slides written in HTML - if you know of one please add a comment!&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, if you create slides using my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;htmlslides&quot;&gt;HTML Slides&lt;&#x2F;a&gt; template, then you can make a decent-ish-looking PDF like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;View your slides in Firefox and open the Print dialog (press &lt;strong&gt;Ctrl-p&lt;&#x2F;strong&gt;).&lt;&#x2F;li&gt;
&lt;li&gt;Select &lt;strong&gt;Print to File&lt;&#x2F;strong&gt; and choose a filename to save to. &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;print-general.png&quot; width=&quot;699&quot; height=&quot;544&quot;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Under &lt;strong&gt;Options&lt;&#x2F;strong&gt; deselect &quot;Ignore Scaling...&quot; and select &quot;--blank--&quot; for all the headers and footers. Ensure &quot;Print Background Colours&quot; and &quot;Print Background Images&quot; are selected. &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;print-options.png&quot; width=&quot;699&quot; height=&quot;544&quot;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Under &lt;strong&gt;Page Setup&lt;&#x2F;strong&gt; set Scale to &lt;strong&gt;70.0&lt;&#x2F;strong&gt; and Orientation to &lt;strong&gt;Landscape&lt;&#x2F;strong&gt;. &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;print-pagesetup.png&quot; width=&quot;699&quot; height=&quot;544&quot;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Select the dropdown next to Paper size and choose &lt;strong&gt;Manage Custom Sizes...&lt;&#x2F;strong&gt; &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;manage-custom-sizes.png&quot; width=&quot;699&quot; height=&quot;539&quot;&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Create a new custom size called &quot;Screen 16:9&quot; with Width &lt;strong&gt;157.5&lt;&#x2F;strong&gt; mm and Height &lt;strong&gt;280.0&lt;&#x2F;strong&gt; mm. Set all the Paper Margins to &lt;strong&gt;5.0&lt;&#x2F;strong&gt; mm. You can modify the name of the custom size by clicking on it in the list on the left. &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;custom-sizes.png&quot; width=&quot;543&quot; height=&quot;430&quot;&gt;&lt;br &#x2F;&gt;
Click Close.&lt;&#x2F;li&gt;
&lt;li&gt;Back in &lt;strong&gt;Page Setup&lt;&#x2F;strong&gt;, make sure your new custom size is selected next to &lt;strong&gt;Paper Size&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Click &lt;strong&gt;Print&lt;&#x2F;strong&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If that does not work well for you, try experimenting with different Scale settings.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Support the Software Freedom Conservancy</title>
          <pubDate>Fri, 24 Jan 2020 13:09:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2020/01/24/support-the-software-freedom-conservancy/</link>
          <guid>https://artificialworlds.net/blog/2020/01/24/support-the-software-freedom-conservancy/</guid>
          <description xml:base="https://artificialworlds.net/blog/2020/01/24/support-the-software-freedom-conservancy/">&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&quot;&gt;Software Freedom Conservancy&lt;&#x2F;a&gt; helps Free&#x2F;Open Source software projects by providing infrastructure, financial structures, and legal help. It is a not-for-profit organisation that is dedicated to software freedom, something that I think is an important prerequisite for a decent world in the future.&lt;&#x2F;p&gt;
&lt;p&gt;Conservancy looks after &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&#x2F;projects&#x2F;current&#x2F;&quot;&gt;lots of projects&lt;&#x2F;a&gt;, including these ones that I personally use: Boost, BusyBox, Etherpad, Git, Godot Engine, Inkscape, phpMyAdmin, QEMU, Racket, Samba, Selenium, Squeak and Wine.&lt;&#x2F;p&gt;
&lt;p&gt;It &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&#x2F;projects&#x2F;services&#x2F;&quot;&gt;provides&lt;&#x2F;a&gt; an easy way for projects to accept donations, hold assets and negotiate contracts, as well as mentoring and legal advice. It also leads the difficult and thankless work to persuade (and force) companies to comply with the GPL (a Free Software license). Without this work a great deal more Free Software would be distributed without the required access to source code, meaning it is not free at all.&lt;&#x2F;p&gt;
&lt;p&gt;Further, it is a key supporter of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.outreachy.org&#x2F;&quot;&gt;Outreachy&lt;&#x2F;a&gt;, which does important work to address the under-representation, systemic bias, and discrimination in the technology industry.&lt;&#x2F;p&gt;
&lt;p&gt;I encourage you to join me and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sfconservancy.org&#x2F;supporter&#x2F;&quot;&gt;support the Software Freedom Conservancy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You might also like to listen to the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;faif.us&#x2F;&quot;&gt;Free As In Freedom&lt;&#x2F;a&gt; podcast.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>KotlinJS: First impressions (formed by writing Snake) video</title>
          <pubDate>Sat, 30 Nov 2019 05:28:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/11/30/kotlinjs-first-impressions-formed-by-writing-snake-video/</link>
          <guid>https://artificialworlds.net/blog/2019/11/30/kotlinjs-first-impressions-formed-by-writing-snake-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/11/30/kotlinjs-first-impressions-formed-by-writing-snake-video/">&lt;p&gt;What it was like to write a snake game in KotlinJS:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;KotlinJS: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3b8f2ef0-6bad-4c43-8101-91af5f37144f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-kotlinjs&#x2F;snake-kotlinjs.html#&quot;&gt;Snake in KotlinJS slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;snake&quot;&gt;github.com&#x2F;andybalaam&#x2F;snake&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Dependency Injection frameworks: reasons to avoid them video</title>
          <pubDate>Fri, 11 Oct 2019 19:51:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/10/11/dependency-injection-frameworks-reasons-to-avoid-them-video/</link>
          <guid>https://artificialworlds.net/blog/2019/10/11/dependency-injection-frameworks-reasons-to-avoid-them-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/10/11/dependency-injection-frameworks-reasons-to-avoid-them-video/">&lt;p&gt;At my job we have done a great deal of work to remove Guice from our codebase. Here I try to explain why we did that, and try to apply my reasoning to dependency injection frameworks in general.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Dependency Injection frameworks: reasons to avoid them&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;364cbe0c-6ae4-48f0-b8bf-2f31d55a00c1&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;dependency-injection-frameworks-reasons-to-avoid&#x2F;dependency-injection-frameworks-reasons-to-avoid.html&quot;&gt;Dependency Injection frameworks: reasons to avoid them slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Coding workshop example worksheets</title>
          <pubDate>Thu, 19 Sep 2019 10:53:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/09/19/coding-workshop-example-worksheets/</link>
          <guid>https://artificialworlds.net/blog/2019/09/19/coding-workshop-example-worksheets/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/09/19/coding-workshop-example-worksheets/">&lt;p&gt;This week we did a coding workshop exercise: 2 teams implemented the different sides of the SMPP protocol (without speaking to each other) and this morning we tried out connecting them together.&lt;&#x2F;p&gt;
&lt;p&gt;We successfully sent a message and received an acknowledgement!&lt;&#x2F;p&gt;
&lt;p&gt;It was a lot of fun and I we learned a surprising amount about SMPP (and quite how ... interesting ... the standard is).&lt;&#x2F;p&gt;
&lt;p&gt;In case they&#x27;re useful to anyone, here are the worksheets I made up: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;coding-challenge-team1.odt&quot;&gt;Team 1 ODT&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;coding-challenge-team1.pdf&quot;&gt;Team 1 PDF&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;coding-challenge-team2.odt&quot;&gt;Team 2 ODT&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;coding-challenge-team2.pdf&quot;&gt;Team 2 PDF&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Idea for a team who are less interested in SMPP (!) - try a similar exercise implementing FTP, which is a nice simple text-based protocol. I did this once and found it extremely rewarding.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building an all-in-one Jar in Gradle with the Kotlin DSL</title>
          <pubDate>Fri, 12 Jul 2019 14:55:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/07/12/building-an-all-in-one-jar-in-gradle-with-the-kotlin-dsl/</link>
          <guid>https://artificialworlds.net/blog/2019/07/12/building-an-all-in-one-jar-in-gradle-with-the-kotlin-dsl/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/07/12/building-an-all-in-one-jar-in-gradle-with-the-kotlin-dsl/">&lt;p&gt;To build a &quot;fat&quot; Jar of your Java or Kotlin project that contains all the dependencies within a single file, you can use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;imperceptiblethoughts.com&#x2F;shadow&#x2F;&quot;&gt;shadow&lt;&#x2F;a&gt; Gradle plugin.&lt;&#x2F;p&gt;
&lt;p&gt;I found it hard to find clear documentation on how it works using the Gradle Kotlin DSL (with a build.gradle.kts instead of build.gradle) so here is how I did it:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat build.gradle.kts
&lt;span style=&quot;color:red;&quot;&gt;import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar&lt;&#x2F;span&gt;

plugins {
    kotlin(&quot;jvm&quot;) version &quot;1.3.41&quot;
    &lt;span style=&quot;color:red;&quot;&gt;id(&quot;com.github.johnrengelman.shadow&quot;) version &quot;5.1.0&quot;&lt;&#x2F;span&gt;
}

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin(&quot;stdlib&quot;))
}

&lt;span style=&quot;color:red;&quot;&gt;tasks.withType&amp;lt;ShadowJar&amp;gt;() {
    manifest {
        attributes[&quot;Main-Class&quot;] = &quot;HelloKt&quot;
    }
}&lt;&#x2F;span&gt;

$ cat src&#x2F;main&#x2F;kotlin&#x2F;Hello.kt
fun main() {
    println(&quot;Hello!&quot;)
}

$ gradle wrapper --gradle-version 5.5
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed

$ .&#x2F;gradlew &lt;span style=&quot;color:red;&quot;&gt;shadowJar&lt;&#x2F;span&gt;
BUILD SUCCESSFUL in 1s
2 actionable tasks: 2 executed

$ java -jar build&#x2F;libs&#x2F;hello&lt;span style=&quot;color:red;&quot;&gt;-all&lt;&#x2F;span&gt;.jar
Hello!&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Creating a self-signed certificate for Apache and connecting to it from Java</title>
          <pubDate>Thu, 02 May 2019 14:10:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/05/02/creating-a-self-signed-certificate-for-apache-and-connecting-to-it-from-java/</link>
          <guid>https://artificialworlds.net/blog/2019/05/02/creating-a-self-signed-certificate-for-apache-and-connecting-to-it-from-java/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/05/02/creating-a-self-signed-certificate-for-apache-and-connecting-to-it-from-java/">&lt;p&gt;Our mission: to create a self-signed certificate for an Apache web server that allows us to connect to it over HTTPS (SSL&#x2F;TLS) from a Java program.&lt;&#x2F;p&gt;
&lt;p&gt;The tricky bit for me was generating a certificate that contains Subject Alternative Names for my server, which is needed to connect to it from Java.&lt;&#x2F;p&gt;
&lt;p&gt;We will use the openssl command.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;creating-a-self-signed-certificate-for-apache-httpd&quot;&gt;Creating a self-signed certificate for Apache HTTPD&lt;&#x2F;h2&gt;
&lt;p&gt;First create a config file cert.conf:&lt;&#x2F;p&gt;
&lt;pre&gt;[ req ]
distinguished_name  = subject
x509_extensions     = x509_ext
prompt = no

[ subject ]
commonName = Example Company

[ x509_ext ]
subjectAltName = @alternate_names

[ alternate_names ]
DNS.1 = &lt;span style=&quot;color: red;&quot;&gt;example.com&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In the above, replace &quot;example.com&quot; with the name you will use for the host when you connect from Java. This is important, because Java requires the name in the certificate to match the name it is using to connect to the server. If you&#x27;re connecting to it as localhost, just put &quot;localhost&quot;. Note: do not include &quot;https:&#x2F;&#x2F;&quot; or any port or path after the hostname, so &quot;example.com:8080&#x2F;mypath&quot; is wrong - it should be just &quot;example.com&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The alternate_names section above gives the &quot;Subject Alternative Names&quot; for this certificate. You can add more as &quot;DNS.2&quot;, &quot;DNS.3&quot;, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Next, generate the server key and self-signed certificate:&lt;&#x2F;p&gt;
&lt;pre&gt;openssl genrsa 2048 &amp;gt; server.key
chmod 400 server.key
openssl req -new -x509 -config cert.conf -nodes -sha256 -days 365 -key server.key -out server.crt&lt;&#x2F;pre&gt;
&lt;p&gt;Now you have two new files: server.key and server.crt. These are the files that will be used by Apache HTTPD, so put them somewhere useful (e.g. inside &#x2F;usr&#x2F;local&#x2F;apache2&#x2F;conf&#x2F;) and refer to them in the Apache config file using keys &quot;SSLCertificateKeyFile&quot; and &quot;SSLCertificateFile&quot; respectively. For more info see the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;httpd.apache.org&#x2F;docs&#x2F;2.4&#x2F;ssl&#x2F;ssl_howto.html&quot;&gt;SSL&#x2F;TLS How-To&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;checking-the-certificate-is-being-used&quot;&gt;Checking the certificate is being used&lt;&#x2F;h2&gt;
&lt;p&gt;Start up your Apache and ensure you can connect to it over HTTPS using curl:&lt;&#x2F;p&gt;
&lt;pre&gt;curl -v --insecure &lt;span style=&quot;color: red;&quot;&gt;https:&#x2F;&#x2F;example.com:8080&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Replace &quot;https:&#x2F;&#x2F;example.com:8080&quot; above with the full URL (this time, include &quot;https:&#x2F;&#x2F;&quot; and the port and path.&lt;&#x2F;p&gt;
&lt;p&gt;To examine the certificate that is being returned, run:&lt;&#x2F;p&gt;
&lt;pre&gt;openssl s_client -showcerts -connect &lt;span style=&quot;color: red;&quot;&gt;example.com:8080&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Replace &quot;example.com:8080&quot; above with hostname and port (no &quot;https:&#x2F;&#x2F; this time!).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;connecting-from-java&quot;&gt;Connecting from Java&lt;&#x2F;h2&gt;
&lt;p&gt;To be able to connect from Java, we need a Trust Store. We can create one in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PKCS_12&quot;&gt;PKCS#12&lt;&#x2F;a&gt; format with:&lt;&#x2F;p&gt;
&lt;pre&gt;openssl pkcs12 -export -passout pass:000000 -out trust.pkcs12 -inkey server.key -in server.crt&lt;&#x2F;pre&gt;
&lt;p&gt;Note: Java 8 onwards is able to use .pkcs12 (PKCS#12) files for its trust store. The old .jks (Java Key Store) format can also be used, but is deprecated.&lt;&#x2F;p&gt;
&lt;p&gt;Now you have a file we can use as a trust store, follow my other article to &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;12&#x2F;07&#x2F;java-https-to-a-server-with-a-self-signed-certificate&#x2F;&quot;&gt;connect from Java over HTTPS with a self-signed certificate&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ACCU Talk &quot;How Kotlin makes your Java code better&quot;</title>
          <pubDate>Wed, 01 May 2019 14:15:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/05/01/accu-talk-how-kotlin-makes-your-java-code-better/</link>
          <guid>https://artificialworlds.net/blog/2019/05/01/accu-talk-how-kotlin-makes-your-java-code-better/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/05/01/accu-talk-how-kotlin-makes-your-java-code-better/">&lt;p&gt;Here is the live version of my talk designed to help you advocate for adopting Kotlin:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=uCJjda6-HIY&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;uCJjda6-HIY&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ACCU Talk &quot;How Git really works&quot;</title>
          <pubDate>Wed, 01 May 2019 14:13:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/05/01/accu-talk-how-git-really-works/</link>
          <guid>https://artificialworlds.net/blog/2019/05/01/accu-talk-how-git-really-works/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/05/01/accu-talk-how-git-really-works/">&lt;p&gt;Here is the talk CB Bailey and I did at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;conference.accu.org&#x2F;&quot;&gt;ACCU Conference 2019&lt;&#x2F;a&gt;. This was the first talk I have done with someone else, and I really enjoyed it:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=N0m42TKk_dc&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;N0m42TKk_dc&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Examples of Kotlin making your Java code better video</title>
          <pubDate>Tue, 23 Apr 2019 21:27:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/04/23/examples-of-kotlin-making-your-java-code-better-video/</link>
          <guid>https://artificialworlds.net/blog/2019/04/23/examples-of-kotlin-making-your-java-code-better-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/04/23/examples-of-kotlin-making-your-java-code-better-video/">&lt;p&gt;People at work suggested Kotlin was &quot;just syntactic sugar&quot;, so I set out to explain how Kotlin can really make better code, and here is the result:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Examples of Kotlin making java code better&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;9bfdbb5b-bca3-48e7-a73d-3e1c09fa159e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;examples-of-kotlin-making-code-better&#x2F;examples-of-kotlin-making-code-better.html#&quot;&gt;Examples of Kotlin making your Java code better slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What is a Monad? Video</title>
          <pubDate>Tue, 23 Apr 2019 21:19:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/04/23/what-is-a-monad-video/</link>
          <guid>https://artificialworlds.net/blog/2019/04/23/what-is-a-monad-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/04/23/what-is-a-monad-video/">&lt;p&gt;Slightly jokey video about what a Monad is, but with a genuine explanation, all in just about 5 minutes. First given as a lightning talk at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;conference.accu.org&#x2F;&quot;&gt;ACCU Conference&lt;&#x2F;a&gt; in 2019.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;What is a Monad?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;563d4d69-ce6a-4165-aa00-f66ed70e3006&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;what-is-a-monad&#x2F;andybalaam-an-important-question.pdf&quot;&gt;What is a Monad? Slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Build with a different Java version (e.g. 11) using Docker</title>
          <pubDate>Tue, 23 Apr 2019 13:59:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/04/23/build-with-a-different-java-version-e-g-11-using-docker/</link>
          <guid>https://artificialworlds.net/blog/2019/04/23/build-with-a-different-java-version-e-g-11-using-docker/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/04/23/build-with-a-different-java-version-e-g-11-using-docker/">&lt;p&gt;To spin up a temporary environment with a different Java version without touching your real environment, try this Docker command:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run -i -t --mount &quot;type=bind,src=$PWD,dst=&#x2F;code&quot; openjdk:11-jdk bash&lt;&#x2F;pre&gt;
&lt;p&gt;(Change &quot;11-jdk&quot; to the version you want as listed on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker-library&#x2F;docs&#x2F;blob&#x2F;master&#x2F;openjdk&#x2F;README.md&quot;&gt;README&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Then you can build the code inside the current directory something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;cd code
.&#x2F;gradlew test&lt;&#x2F;pre&gt;
&lt;p&gt;Or similar for other build tools, although you may need to install them first.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape t-shirts</title>
          <pubDate>Sat, 13 Apr 2019 13:45:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/04/13/rabbit-escape-t-shirts/</link>
          <guid>https://artificialworlds.net/blog/2019/04/13/rabbit-escape-t-shirts/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/04/13/rabbit-escape-t-shirts/">&lt;p&gt;If you&#x27;d like to help promote &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, my free software Android&#x2F;PC game, how about a t-shirt?&lt;&#x2F;p&gt;
&lt;p&gt;Use the link below to design your shirt:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.streetshirts.co.uk&#x2F;design-your-own-t-shirts?designkey=PFC%5BSSDShAF8aEI9J4vWdpGhnqwdJPlPme%5D&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rabbit-escape-t-shirt.png&quot; alt=&quot;Rabbit Escape t-shirt&quot; width=&quot;389&quot; height=&quot;438&quot; class=&quot;alignnone size-full wp-image-2698&quot;&gt;&lt;br &#x2F;&gt;
Rabbit Escape t-shirt&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note: there&#x27;s no profit in this for us - it&#x27;s just for promotion. If you&#x27;d like to contribute financially, please &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;donate on Patreon&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=net.artificialworlds.rabbitescape&amp;amp;hl=en_US&quot;&gt;buy a copy on the Play Store&lt;&#x2F;a&gt;. Thank you!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheduling a task in Java within a CompletableFuture</title>
          <pubDate>Fri, 05 Apr 2019 15:55:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/04/05/scheduling-a-task-in-java-within-a-completablefuture/</link>
          <guid>https://artificialworlds.net/blog/2019/04/05/scheduling-a-task-in-java-within-a-completablefuture/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/04/05/scheduling-a-task-in-java-within-a-completablefuture/">&lt;p&gt;When we want to do something later in our Java code, we often turn to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.oracle.com&#x2F;en&#x2F;java&#x2F;javase&#x2F;11&#x2F;docs&#x2F;api&#x2F;java.base&#x2F;java&#x2F;util&#x2F;concurrent&#x2F;ScheduledExecutorService.html&quot;&gt;ScheduledExecutorService&lt;&#x2F;a&gt;. This class has a method called schedule(), and we can pass it some code to be run later like this:&lt;&#x2F;p&gt;
&lt;pre&gt;ScheduledExecutorService executor =
    Executors.newScheduledThreadPool(4);
executor.schedule(
    () -&amp;gt; {System.out.println(&quot;..later&quot;);},
    1,
    TimeUnit.SECONDS
);
System.out.println(&quot;do...&quot;);
&#x2F;&#x2F; (Don&#x27;t forget to shut down the executor later...)&lt;&#x2F;pre&gt;
&lt;p&gt;The above code prints &quot;do...&quot; and then one second later it prints &quot;...later&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;We can even write code that does some work and returns a result in a similar way:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; (Make the executor as above.)
ScheduledFuture future = executor.schedule(
    () -&amp;gt; 10 + 25, 1, TimeUnit.SECONDS);
System.out.println(&quot;answer=&quot; + future.get())&lt;&#x2F;pre&gt;
&lt;p&gt;The above code prints &quot;answer=35&quot;. When we call get() it blocks waiting for the scheduler to run the task and mark the ScheduledFuture as complete, and then returns the answer to the sum (10 + 25) when it is ready.&lt;&#x2F;p&gt;
&lt;p&gt;This is all very well, but you may note that the Future returned from schedule() is a ScheduledFuture, and a ScheduledFuture is not a CompletableFuture.&lt;&#x2F;p&gt;
&lt;p&gt;Why do you care? Well, you might care if you want to do something after the scheduled task is completed. Of course, you can call get(), and block, and then do something, but if you want to react asynchronously without blocking, this wonâ€™t work.&lt;&#x2F;p&gt;
&lt;p&gt;The normal way to run some code after a Future has completed is to call one of the &quot;then*&quot; or &quot;when*&quot; methods on the Future, but these methods are only available on CompletableFuture, not ScheduledFuture.&lt;&#x2F;p&gt;
&lt;p&gt;Never fear, we have figured this out for you. We present a small wrapper for schedule that transforms your ScheduledFuture into a CompletableFuture. Hereâ€™s how to use it:&lt;&#x2F;p&gt;
&lt;pre&gt;CompletableFuture&amp;lt;Integer&amp;gt; future =
    ScheduledCompletable.schedule(
        executor,
        () -&amp;gt; 10 + 25,
        1,
        TimeUnit.SECONDS
     );
future.thenAccept(
    answer -&amp;gt; {System.out.println(answer);}
);
System.out.println(&quot;Answer coming...&quot;)&lt;&#x2F;pre&gt;
&lt;p&gt;The above code prints &quot;Answer coming...&quot; and then &quot;35&quot;, so we can see that we donâ€™t block the main thread waiting for the answer to come back.&lt;&#x2F;p&gt;
&lt;p&gt;So far, we have scheduled a synchronous task to run in the background after a delay, and wrapped its result in a CompletableFuture to allow us to chain more tasks after it.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, what we often want to do is schedule a delayed task that is itself asynchronous, and already returns a CompletableFuture. In this case it feels particularly natural to get the result back as a CompletableFuture, but with the current ScheduledExecutorService interface we canâ€™t easily do it.&lt;&#x2F;p&gt;
&lt;p&gt;Itâ€™s OK, weâ€™ve figured that out too:&lt;&#x2F;p&gt;
&lt;pre&gt;Supplier&amp;lt;CompletableFuture&amp;lt;Integer&amp;gt;&amp;gt; asyncTask = () -&amp;gt;
    CompletableFuture.completedFuture(10 + 25);
CompletableFuture&amp;lt;Integer&amp;gt; future =
    ScheduledCompletable.scheduleAsync(
        executor, asyncTask, 1, TimeUnit.SECONDS);
future.thenAccept(
    answer -&amp;gt; {System.out.println(answer);}
);
System.out.println(&quot;Answer coming...&quot;)&lt;&#x2F;pre&gt;
&lt;p&gt;The above code prints &quot;Answer coming...&quot; and then &quot;35&quot;, so we can see that our existing asynchronous task was scheduled in the background, and we didnâ€™t have to block the main thread waiting for it. Also, under the hood, we are not blocking the ScheduledExecutorService&#x27;s thread (from its pool) while the async task is running &quot; that task just runs in whatever thread it was assigned when it was created. (Note: in our example we donâ€™t really run an async task at all, but just immediately return a completed Future, but this does work for real async tasks.)&lt;&#x2F;p&gt;
&lt;p&gt;I know youâ€™re wondering how we achieved all this. First, hereâ€™s how we run a simple blocking task in the background and wrap it in a CompletableFuture:&lt;&#x2F;p&gt;
&lt;pre&gt;public static &amp;lt;T&amp;gt; CompletableFuture&amp;lt;T&amp;gt; schedule(
    ScheduledExecutorService executor,
    Supplier&amp;lt;T&amp;gt; command,
    long delay,
    TimeUnit unit
) {
    CompletableFuture&amp;lt;T&amp;gt; completableFuture = new CompletableFuture&amp;lt;&amp;gt;();
    executor.schedule(
        (() -&amp;gt; {
            try {
                return completableFuture.complete(command.get());
            } catch (Throwable t) {
                return completableFuture.completeExceptionally(t);
            }
        }),
        delay,
        unit
    );
    return completableFuture;
}&lt;&#x2F;pre&gt;
&lt;p&gt;And hereâ€™s how we delay execution of an async task but still return its result in a CompletableFuture:&lt;&#x2F;p&gt;
&lt;pre&gt;public static &amp;lt;T&amp;gt; CompletableFuture&amp;lt;T&amp;gt; scheduleAsync(
    ScheduledExecutorService executor,
    Supplier&amp;lt;CompletableFuture&amp;lt;T&amp;gt;&amp;gt; command,
    long delay,
    TimeUnit unit
) {
    CompletableFuture&amp;lt;T&amp;gt; completableFuture = new CompletableFuture&amp;lt;&amp;gt;();
    executor.schedule(
        (() -&amp;gt; {
            command.get().thenAccept(
                t -&amp;gt; {completableFuture.complete(t);}
            )
            .exceptionally(
                t -&amp;gt; {completableFuture.completeExceptionally(t);return null;}
            );
        }),
        delay,
        unit
    );
    return completableFuture;
}&lt;&#x2F;pre&gt;
&lt;p&gt;Note that this should all work to run methods like exceptionally(), thenAccept(), whenComplete() etc.&lt;&#x2F;p&gt;
&lt;p&gt;Feedback and improvements welcome!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape 0.12 out now, with water</title>
          <pubDate>Tue, 05 Mar 2019 00:30:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/03/05/rabbit-escape-0-12-out-now-with-water/</link>
          <guid>https://artificialworlds.net/blog/2019/03/05/rabbit-escape-0-12-out-now-with-water/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/03/05/rabbit-escape-0-12-out-now-with-water/">&lt;p&gt;The newest feature of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, water, has been brewing a long time, but we now think it&#x27;s ready:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;water.gif&quot; alt=&quot;&quot; width=&quot;384&quot; height=&quot;272&quot; class=&quot;alignnone size-full wp-image-2689&quot;&gt;
&lt;p&gt;Water can flow, it can put out fires, and it can drown rabbits.&lt;&#x2F;p&gt;
&lt;p&gt;Rabbots seem to be immune though...&lt;&#x2F;p&gt;
&lt;p&gt;Check out the 20 new levels we have released! (This makes a total of 180 levels.)&lt;&#x2F;p&gt;
&lt;p&gt;It works on Windows, Linux, Mac OS and Android.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;What is Rabbit Escape?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a787f7d6-cc29-4bc7-9920-b26723de42ec&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Convert a video to a GIF with reasonable colours</title>
          <pubDate>Tue, 05 Mar 2019 00:17:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/03/05/convert-a-video-to-a-gif-with-reasonable-colours/</link>
          <guid>https://artificialworlds.net/blog/2019/03/05/convert-a-video-to-a-gif-with-reasonable-colours/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/03/05/convert-a-video-to-a-gif-with-reasonable-colours/">&lt;p&gt;Here&#x27;s a little script I wrote to avoid copy-pasting the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;556029&#x2F;how-do-i-convert-a-video-to-gif-using-ffmpeg-with-reasonable-quality&quot;&gt;ffmpeg command from superuser&lt;&#x2F;a&gt; every time I needed it.&lt;&#x2F;p&gt;
&lt;p&gt;It converts a video to a GIF file by pre-calculating a good palette, then using that palette.&lt;&#x2F;p&gt;
&lt;p&gt;Usage:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;to_gif input.mp4 output.gif&lt;&#x2F;pre&gt;
&lt;p&gt;The file to_gif (which should be executable):&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -e
set -u

# Credit: https:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;556029&#x2F;how-do-i-convert-a-video-to-gif-using-ffmpeg-with-reasonable-quality

INPUT=&quot;$1&quot;
OUTPUT=&quot;$2&quot;

PALETTE=$(mktemp --suffix=.png)

ffmpeg -y -i &quot;${INPUT}&quot; -vf palettegen &quot;${PALETTE}&quot;
ffmpeg -y -i &quot;${INPUT}&quot; -i &quot;${PALETTE}&quot; \
    -filter_complex &quot;fps=15,paletteuse&quot; &quot;${OUTPUT}&quot;

rm -f &quot;${PALETTE}&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;Note: you might want to modify the number after fps= to adjust how fast the video plays.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: changed to use mktemp instead of tempfile.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: and here is how I add it to the context menu in Caja:&lt;&#x2F;p&gt;
&lt;p&gt;Place the above file in $HOME&#x2F;bin&#x2F;to_gif.&lt;&#x2F;p&gt;
&lt;p&gt;Create a file ~&#x2F;.config&#x2F;caja&#x2F;scripts&#x2F;to_gif (don&#x27;t forget to make it executable):&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

mate-terminal -x &quot;${HOME}&#x2F;bin&#x2F;to_gif&quot; &quot;$1&quot; &quot;$1&quot;.gif&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Gradle: what is a task, and how can I make a task depend on another task?</title>
          <pubDate>Wed, 27 Feb 2019 15:59:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/27/gradle-what-is-a-task-and-how-can-i-make-a-task-depend-on-another-task/</link>
          <guid>https://artificialworlds.net/blog/2019/02/27/gradle-what-is-a-task-and-how-can-i-make-a-task-depend-on-another-task/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/27/gradle-what-is-a-task-and-how-can-i-make-a-task-depend-on-another-task/">&lt;p&gt;In an insane world, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gradle.org&#x2F;&quot;&gt;Gradle&lt;&#x2F;a&gt; sometimes seems like the sanest choice for building a Java or Kotlin project.&lt;&#x2F;p&gt;
&lt;p&gt;But what on Earth does all the stuff inside build.gradle actually mean?&lt;&#x2F;p&gt;
&lt;p&gt;And when does my code run?&lt;&#x2F;p&gt;
&lt;p&gt;And how do you make a task?&lt;&#x2F;p&gt;
&lt;p&gt;And how do you persuade a task to depend on another task?&lt;&#x2F;p&gt;
&lt;p&gt;[Related: &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;06&#x2F;03&#x2F;clever-things-people-do-in-groovy-so-you-have-to-know-about-them-video&#x2F;&quot;&gt;Clever things people do in Groovy so you have to know about them&lt;&#x2F;a&gt;]&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up&quot;&gt;Setting up&lt;&#x2F;h2&gt;
&lt;p&gt;To use Gradle, get hold of any version of it for long enough to create a local gradlew file, and then use that.&lt;&#x2F;p&gt;
&lt;pre&gt;$ mkdir gradle-experiments
$ cd gradle-experiments
$ sudo apt install gradle  # Briefly install the system version of gradle
...
$ gradle wrapper --gradle-version=5.2.1
$ sudo apt remove gradle   # Optional - uninstalls the system version
$ .&#x2F;gradlew tasks
... If all is good, this should ...
... print a list of available tasks. ...
&lt;&#x2F;pre&gt;
&lt;p&gt;It is normal for gradlew and the whole gradle&#x2F; directory it creates to be checked into source control. This means everyone who fetches the code from source control will have a predictable Gradle version.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-build-gradle&quot;&gt;What is build.gradle?&lt;&#x2F;h2&gt;
&lt;p&gt;build.gradle is a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.groovy-lang.org&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt; program that Gradle runs within a context that it has set up for you. That context means that you are actually calling methods of a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gradle.org&#x2F;4.9&#x2F;dsl&#x2F;org.gradle.api.Project.html&quot;&gt;Project&lt;&#x2F;a&gt; object, and modifying its properties. The fact that Groovy lets you miss out a lot of punctuation makes that harder to see, but it&#x27;s true.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing to get your head around is that Gradle actually runs your code immediately, so if your build.gradle looks like this (and only this):&lt;&#x2F;p&gt;
&lt;pre&gt;println(&quot;Hello&quot;)&lt;&#x2F;pre&gt;
&lt;p&gt;when you run Gradle your code runs:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;gradlew -q
Hello
... more guff ...&lt;&#x2F;pre&gt;
&lt;p&gt;So that code runs even if you don&#x27;t ask Gradle to run a task containing that code. It runs at &quot;configuration time&quot; - i.e. when Gradle is understanding your build.gradle file. Actually, &quot;understanding&quot; it means &lt;em&gt;executing&lt;&#x2F;em&gt; it.&lt;&#x2F;p&gt;
&lt;p&gt;Remember when I said this code runs in the context of a Project? What that means is that if you have something like this in your build.gradle:&lt;&#x2F;p&gt;
&lt;pre&gt;repositories {
    jcenter()
}&lt;&#x2F;pre&gt;
&lt;p&gt;what it really means is something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;project.repositories(
    {
        it.jcenter()
    }
)&lt;&#x2F;pre&gt;
&lt;p&gt;You are calling the repositories method on the project object. The argument to the repositories method is a Groovy closure, which is a blob of code that will get run later. I&#x27;ve used the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;groovy-lang.org&#x2F;closures.html&quot;&gt;magic&lt;&#x2F;a&gt; it name above to demonstrate that jcenter is just a method being called on the object that is the context for the closure when it is run.&lt;&#x2F;p&gt;
&lt;p&gt;When does it run? Let&#x27;s find out:&lt;&#x2F;p&gt;
&lt;pre&gt;println(&quot;before&quot;)
project.repositories( {
    println(&quot;within&quot;)
    jcenter()
})
println(&quot;after&quot;)&lt;&#x2F;pre&gt;
&lt;pre&gt;$ .&#x2F;gradlew -q
before
within
after
... more guff ...&lt;&#x2F;pre&gt;
&lt;p&gt;This surprised me - it means the closure you pass in to repositories is actually run immediately, as part of running repositories, before execution gets to the line after that call.&lt;&#x2F;p&gt;
&lt;p&gt;As we&#x27;ll see later, some closures you create do not run immediately like this one.&lt;&#x2F;p&gt;
&lt;p&gt;Once you know that build.gradle is actually modifying a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gradle.org&#x2F;4.9&#x2F;dsl&#x2F;org.gradle.api.Project.html&quot;&gt;Project&lt;&#x2F;a&gt; object, you have starting point for understanding the Gradle reference documentation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-do-you-make-a-task&quot;&gt;How do you make a task?&lt;&#x2F;h2&gt;
&lt;p&gt;You probably shouldn&#x27;t do it very often, but it was instructive for me to understand how to make my own custom task. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre&gt;tasks.register(&quot;mytask&quot;) {
    doLast {
        println(&quot;running mytask&quot;)
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This creates a new task by calling the register method on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gradle.org&#x2F;4.9&#x2F;dsl&#x2F;org.gradle.api.Project.html#org.gradle.api.Project:tasks&quot;&gt;tasks&lt;&#x2F;a&gt; property of the Project object. Register takes two arguments: a name for the task (&quot;mytask&quot; here), and a closure with some code in it to run when we decide we need this task. That closure gets run in a context that &lt;em&gt;can&#x27;t&lt;&#x2F;em&gt; see the Project object, but instead can see a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gradle.org&#x2F;4.9&#x2F;javadoc&#x2F;org&#x2F;gradle&#x2F;api&#x2F;Task.html&quot;&gt;Task&lt;&#x2F;a&gt; object which it is helping to make. That Task object has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gradle.org&#x2F;4.9&#x2F;javadoc&#x2F;org&#x2F;gradle&#x2F;api&#x2F;Task.html#doLast-org.gradle.api.Action-&quot;&gt;doLast&lt;&#x2F;a&gt; method that we call, passing it a closure that will be run when the task is actually executed (&lt;strong&gt;not&lt;&#x2F;strong&gt; immediately).&lt;&#x2F;p&gt;
&lt;p&gt;If we remove some of the syntactic sugar the above build.gradle looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;tasks.register(
    &quot;mytask&quot;,
    {
        it.doLast(
            {
                println(&quot;running mytask&quot;)
            }
        )
    }
)&lt;&#x2F;pre&gt;
&lt;p&gt;Above we can see that register really does take two arguments as I said above - the first version uses a Groovy feature where if you miss out the last argument and write a closure immediately afterwards the closure is passed as the last argument. Confusing, eh?&lt;&#x2F;p&gt;
&lt;p&gt;Again, notice that doLast is a method on the Task object that is implicitly available when the closure is run.&lt;&#x2F;p&gt;
&lt;p&gt;So we have created a task that we can run:&lt;&#x2F;p&gt;
&lt;pre&gt; .&#x2F;gradlew -q mytask
running mytask&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;how-do-you-make-a-task-depend-on-another-task&quot;&gt;How do you make a task depend on another task?&lt;&#x2F;h2&gt;
&lt;p&gt;If I want to run my code formatting before my compile (for example) I sometimes need to modify a task to make it depend on another one. This can done for tasks you create or for pre-existing ones. Here&#x27;s an example:&lt;&#x2F;p&gt;
&lt;pre&gt;plugins {
    id &quot;java&quot;
}
tasks.register(&quot;mytask&quot;) {
    doLast {
        println(&quot;running mytask&quot;)
    }
}
&lt;span style=&quot;color:red;&quot;&gt;compileJava {
    dependsOn tasks.named(&quot;mytask&quot;)
}&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, calling the plugins method on the Project at the top with a closure that ran the id method on something modified the Project so that it had a new method called compileJava which we called at the bottom, passing it a closure to run. That closure ran in the context of a Task object (similar to when we created a task, but now allow us to modify a pre-existing one). We called the dependsOn method of the Task object, passing in another Task object which we had got by calling the named method on the tasks object.&lt;&#x2F;p&gt;
&lt;p&gt;[Side note: the register method actually returns a Task object that we could have passed to dependsOn without looking it up again using named, but Groovy doesn&#x27;t provide a very convenient way of holding on to that reference, so we didn&#x27;t do it. The Kotlin example below shows that this is quite simple in Kotlin.]&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-do-i-do-all-this-in-kotlin&quot;&gt;How do I do all this in Kotlin?&lt;&#x2F;h2&gt;
&lt;p&gt;Because one DSL that hides what&#x27;s really going on wasn&#x27;t enough for you, Gradle now provides a second DSL that hides what&#x27;s going on in subtly different ways, which is a program written in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kotlinlang.org&#x2F;&quot;&gt;Kotlin&lt;&#x2F;a&gt; instead of Groovy. This is marginally better, because Kotlin doesn&#x27;t let you do quite so many stupid tricks as Groovy does.&lt;&#x2F;p&gt;
&lt;p&gt;Below are all our examples in Kotlin. You get started exactly the same way, by following &quot;Setting up&quot; above. Remember to name your build file build.gradle.kts.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;say-hello-in-gradle-kotlin&quot;&gt;Say hello in Gradle Kotlin&lt;&#x2F;h3&gt;
&lt;pre&gt;println(&quot;Hello&quot;)&lt;&#x2F;pre&gt;
&lt;p&gt;This is identical to the Groovy version.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;use-jcenter-repo-in-gradle-kotlin&quot;&gt;Use jcenter repo in Gradle Kotlin&lt;&#x2F;h3&gt;
&lt;pre&gt;repositories {
    jcenter()
}&lt;&#x2F;pre&gt;
&lt;p&gt;This is identical to the Groovy version, and with the same meaning: repositories is a method on the implicitly-available Project object.&lt;&#x2F;p&gt;
&lt;p&gt;The &quot;unsugared&quot; version looks like this in Kotlin:&lt;&#x2F;p&gt;
&lt;pre&gt;this.repositories(
    {
        this.jcenter()
    }
)&lt;&#x2F;pre&gt;
&lt;p&gt;[Note that the word this is used to access the implicit context. The word it has a different meaning in Kotlin from in Groovy. In Groovy it means the implicit context, but in Kotlin it means the first argument. We didn&#x27;t pass any arguments to jcenter when we called it, so we can&#x27;t use it, but we were being run in a context, which we can refer to using this. Simple. huh?]&lt;&#x2F;p&gt;
&lt;h3 id=&quot;execution-order-in-gradle-kotlin&quot;&gt;Execution order in Gradle Kotlin&lt;&#x2F;h3&gt;
&lt;p&gt;We this build.gradle.kts:&lt;&#x2F;p&gt;
&lt;pre&gt;println(&quot;before&quot;)
project.repositories( {
    println(&quot;within&quot;)
    jcenter()
})
println(&quot;after&quot;)&lt;&#x2F;pre&gt;
&lt;p&gt;We see this behaviour:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;gradlew -q
before
within
after&lt;&#x2F;pre&gt;
&lt;p&gt;which is all identical to the Groovy version.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;making-a-new-task-in-gradle-kotlin&quot;&gt;Making a new task in Gradle Kotlin&lt;&#x2F;h3&gt;
&lt;pre&gt;tasks.register(&quot;mytask&quot;) {
    doLast {
        println(&quot;running mytask&quot;)
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that Kotlin lets you do the same trick as Groovy: providing an extra argument to a function that is a closure by writing it immediately after it looks like you&#x27;ve finished calling it. It&#x27;s good for people who dislike closing brackets hanging around longer than they&#x27;re welcome. As someone who likes Lisp, I&#x27;m OK with closing brackets, but what do I know?&lt;&#x2F;p&gt;
&lt;p&gt;The above is identical to the Groovy version, but slightly different when unsugared:&lt;&#x2F;p&gt;
&lt;pre&gt;tasks.register(
    &quot;mytask&quot;,
    {
        this.doLast(
            {
                println(&quot;running mytask&quot;)
            }
        )
    }
)&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;one-task-depending-on-another-in-gradle-kotlin&quot;&gt;One task depending on another in Gradle Kotlin&lt;&#x2F;h3&gt;
&lt;pre&gt;plugins {
    java
}
val mytask = tasks.register(&quot;mytask&quot;) {
    doLast {
        println(&quot;running mytask&quot;)
    }
}
&lt;span style=&quot;color:red;&quot;&gt;tasks.compileJava {
    dependsOn(mytask)
}&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This differs slightly from the Groovy version, even though the meaning is the same: we start off in the context of a Project object that we call methods on.&lt;&#x2F;p&gt;
&lt;p&gt;The code to make one task depend on another gets hold of the Task object called compileJava from inside the tasks property of the Project, and calls it (because it&#x27;s a callable object). We pass in a closure that runs in the context of this Task object, calling its dependsOn method, and passing in a reference to the mytask object, which is a Task and was created in the code above.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;corrections-and-clarifications-welcome&quot;&gt;Corrections and clarifications welcome&lt;&#x2F;h2&gt;
&lt;p&gt;The above is what I have worked out by experimentation and trying to read the Gradle documentation. Please add comments that clear up confusions and correct mistakes.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Python Async basics video (100 million HTTP requests)</title>
          <pubDate>Tue, 26 Feb 2019 21:48:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/26/python-async-basics-video-100-million-http-requests/</link>
          <guid>https://artificialworlds.net/blog/2019/02/26/python-async-basics-video-100-million-http-requests/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/26/python-async-basics-video-100-million-http-requests/">&lt;p&gt;I found something difficult in Python, which was a bit of a first, so I wrote a whole blog series about it, and now a whole video:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Python Async basics video (100 million HTTP requests)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fd498758-9db6-44bf-bfe9-3f6416ba6f2c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;python-async&#x2F;python-async.html&quot;&gt;Python Async Basics slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Blog posts: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;basic-ideas-of-python-3-asyncio-concurrency&#x2F;&quot;&gt;asyncio basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;&quot;&gt;large numbers in parallel&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;parallel HTTP requests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;adding to stdlib&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Keybase chat bot in 10 lines of bash</title>
          <pubDate>Thu, 14 Feb 2019 09:08:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/14/keybase-chat-bot-in-10-lines-of-bash/</link>
          <guid>https://artificialworlds.net/blog/2019/02/14/keybase-chat-bot-in-10-lines-of-bash/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/14/keybase-chat-bot-in-10-lines-of-bash/">&lt;p&gt;I&#x27;ve been getting very excited about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;keybase.io&quot;&gt;keybase.io&lt;&#x2F;a&gt; recently, not least because it offers secure conversation, and you can have bots.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote a quick bot to simulate Arnold Schwarzenegger which I thought I&#x27;d share to demonstrate how easy it is. It is based on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;keybase.io&#x2F;docs&#x2F;command_line&quot;&gt;keybase command line tool&lt;&#x2F;a&gt; (which comes with the desktop client as standard) and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stedolan.github.io&#x2F;jq&#x2F;&quot;&gt;jq&lt;&#x2F;a&gt;, the brilliant command-line JSON manipulator.&lt;&#x2F;p&gt;
&lt;p&gt;For this to work, you need to have the keybase command installed and working, and you need jq.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the bot:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash
CHANNEL=mychannel
keybase chat api-listen | while read L; do
{
    OUT=$(jq --raw-output &#x27;select(.type == &quot;chat&quot;)|select(.msg.content.text.body|startswith(&quot;!arnie &quot;))| .msg.content.text.body | &quot;*&quot; + ltrimstr(&quot;!arnie &quot;) + &quot;*&quot;&#x27; &amp;lt;&amp;lt;&amp;lt; &quot;$L&quot;)
    if [ &quot;${OUT}&quot; != &quot;&quot; ]; then
    {
        keybase chat send &quot;${CHANNEL}&quot; &quot;${OUT}&quot;
    }; fi
}; done&lt;&#x2F;pre&gt;
&lt;p&gt;and here&#x27;s it working:&lt;&#x2F;p&gt;
&lt;pre&gt;andy&amp;gt; !arnie Do eet do eet now!!!
andy&amp;gt; &lt;strong&gt;Do eet do eet now!!!&lt;&#x2F;strong&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note: here the bot is pretending to be me. To do this nicely, you will want a different account for the bot, but you get the idea.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously, I am now working on a comprehensive bot framework in Rust. Watch this space.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Performance of Java 2D drawing operations (part 3: image opacity)</title>
          <pubDate>Mon, 11 Feb 2019 23:51:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/11/performance-of-java-2d-drawing-operations-part-3-image-opacity/</link>
          <guid>https://artificialworlds.net/blog/2019/02/11/performance-of-java-2d-drawing-operations-part-3-image-opacity/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/11/performance-of-java-2d-drawing-operations-part-3-image-opacity/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;04&#x2F;performance-of-java-2d-drawing-operations&#x2F;&quot;&gt;operations&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;08&#x2F;performance-of-java-2d-drawing-operations-part-2-image-issues&#x2F;&quot;&gt;images&lt;&#x2F;a&gt;, opacity&lt;&#x2F;p&gt;
&lt;p&gt;Not because I was enjoying it, I seemed compelled to continue my quest to understand the performance of various Java 2D drawing operations. I&#x27;m hoping to make my game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; faster, especially on the Raspberry Pi, so you may see another post sometime actually trying this stuff out on a Pi.&lt;&#x2F;p&gt;
&lt;p&gt;But for now, here are the results of my investigation into how different patterns of opacity in images affects rendering performance.&lt;&#x2F;p&gt;
&lt;p&gt;You can find the code here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Images with partially-opaque pixels are no slower than those with fully-opaque pixels&lt;&#x2F;li&gt;
&lt;li&gt;Large transparent areas in images are drawn quite quickly, but transparent pixels mixed with non-transparent are slow&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;advice&quot;&gt;Advice&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Still avoid any transparency whenever possible&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s relatively OK to use large transparent areas on images (e.g. a fixed-size animation where a character moves through the image)&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t bother restricting pixels to be either fully transparent or fully opaque - partially-opaque is fine&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;opacity-patterns-in-images&quot;&gt;Opacity patterns in images&lt;&#x2F;h2&gt;
&lt;p&gt;Non-transparent images drew at 76 FPS, and transparent ones dropped to 45 FPS.&lt;&#x2F;p&gt;
&lt;p&gt;I went further into investigating transparency by creating images that were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;All pixels 50% opacity (34 FPS)&lt;&#x2F;li&gt;
&lt;li&gt;Half pixels 0% opacity, half 100%, mixed up (34 FPS)&lt;&#x2F;li&gt;
&lt;li&gt;Double the size of the original image, but the extra area is fully transparent, and the original area is non-transparent (41 FPS)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I concluded that partial-opacity is not important to performance compared with full-opacity, but that large areas of transparency are relatively fast compared with images with complex patterns of transparency and opacity.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numbers&quot;&gt;Numbers&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;transparency-and-opacity&quot;&gt;Transparency and opacity&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;transparency-results-1.png&quot; alt=&quot;&quot; width=&quot;823&quot; height=&quot;300&quot; class=&quot;alignnone size-full wp-image-2647&quot;&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;large nothing&lt;&#x2F;td&gt;&lt;td&gt;90&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;76&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages transparentimages&lt;&#x2F;td&gt;&lt;td&gt;45&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages transparent50pcimages&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages transparent0pc100pcimages&lt;&#x2F;td&gt;&lt;td&gt;34&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages transparentareaimages&lt;&#x2F;td&gt;&lt;td&gt;41&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;feedback-please&quot;&gt;Feedback please&lt;&#x2F;h2&gt;
&lt;p&gt;Please do get back to me with tips about how to improve the performance of my experimental code.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to log &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;issues&quot;&gt;issues&lt;&#x2F;a&gt;, make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;merge_requests&quot;&gt;merge requests&lt;&#x2F;a&gt; or add comments to the blog post.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Performance of Java 2D drawing operations (part 2: images)</title>
          <pubDate>Fri, 08 Feb 2019 02:05:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/08/performance-of-java-2d-drawing-operations-part-2-image-issues/</link>
          <guid>https://artificialworlds.net/blog/2019/02/08/performance-of-java-2d-drawing-operations-part-2-image-issues/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/08/performance-of-java-2d-drawing-operations-part-2-image-issues/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;04&#x2F;performance-of-java-2d-drawing-operations&#x2F;&quot;&gt;operations&lt;&#x2F;a&gt;, images, &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;11&#x2F;performance-of-java-2d-drawing-operations-part-3-image-opacity&#x2F;&quot;&gt;opacity&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In my &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;04&#x2F;performance-of-java-2d-drawing-operations&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; I examined the performance of various drawing operations in Java 2D rendering. Here I look at some specifics around rendering images, with an eye to finding optimisations I can apply to my game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can find the code here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Drawing images with transparent sections is very slow&lt;&#x2F;li&gt;
&lt;li&gt;Drawing one large image is slower than drawing many small images covering the same area(!)&lt;&#x2F;li&gt;
&lt;li&gt;Drawing images &lt;strong&gt;outside the screen&lt;&#x2F;strong&gt; is &lt;strong&gt;slower than not drawing them&lt;&#x2F;strong&gt; at all (but faster than drawing them onto a visible area)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;advice&quot;&gt;Advice&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Avoid transparent images where possible&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t bother pre-rendering your background tiles onto a single image&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t draw images that are off-screen&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;images-with-transparency&quot;&gt;Images with transparency&lt;&#x2F;h2&gt;
&lt;p&gt;All the images I used were PNG files with a transparency layer, but in most of my experiments there were no transparent pixels. When I used images with transparent pixels the frame rate was much slower, dropping from 78 to 46 FPS. So using images with transparent pixels causes a significant performance hit.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d be grateful if someone who knows more about it can recommend how to improve my program to reduce this impact - I suspect there may be tricks I can do around setComposite or setRenderingHint or enabling&#x2F;encouraging hardware acceleration.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;composite-images&quot;&gt;Composite images&lt;&#x2F;h2&gt;
&lt;p&gt;I assumed that drawing a single image would be much faster than covering the same area of the screen by drawing lots of small images. In fact, the result was the opposite: drawing lots of small images was much faster than drawing a single image covering the same area.&lt;&#x2F;p&gt;
&lt;p&gt;The code for a single image is:&lt;&#x2F;p&gt;
&lt;pre&gt;g2d.drawImage(
    singleLargeImage,
    10,
    10,
    null
)&lt;&#x2F;pre&gt;
&lt;p&gt;and for the small images it is:&lt;&#x2F;p&gt;
&lt;pre&gt;for (y in 0 until 40)
{
    for (x in 0 until 60)
    {
        g2d.drawImage(
            compositeImages[(y*20 + x) % compositeImages.size],
            10 + (20 * x),
            10 + (20 * y),
            null
        )
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;The single large image was rendered at 74 FPS, whereas covering the same area using repeated copies of 100 images was rendered at 80 FPS. I ran this test several times because I found the result surprising, and it was consistent every time.&lt;&#x2F;p&gt;
&lt;p&gt;I have to assume some caching (possibly via accelerated graphics) of the small images is the explanation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;drawing-images-off-the-side-of-the-screen&quot;&gt;Drawing images off the side of the screen&lt;&#x2F;h2&gt;
&lt;p&gt;Drawing images off the side of the screen was faster than drawing them in a visible area, but slower than not drawing them at all. I tested this by adding 10,000 to the x and y positions of the images being drawn (I also tested subtracting 10,000 with similar results). Not drawing any images ran at 93 FPS, drawing images on-screen at 80 FPS, and drawing them off-screen only 83 FPS, meaning drawing images off the side takes significant time.&lt;&#x2F;p&gt;
&lt;p&gt;Advice: check whether images are on-screen, and avoid drawing them if not.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numbers&quot;&gt;Numbers&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;transparency&quot;&gt;Transparency&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;transparency-results.png&quot; alt=&quot;&quot; width=&quot;782&quot; height=&quot;316&quot; class=&quot;alignnone size-full wp-image-2637&quot;&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;large nothing&lt;&#x2F;td&gt;&lt;td&gt;95&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;78&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages transparentimages&lt;&#x2F;td&gt;&lt;td&gt;46&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;composite-images-1&quot;&gt;Composite images&lt;&#x2F;h3&gt;
&lt;p&gt;(Lots of small images covering an area, or a single larger image.)&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;composite-results.png&quot; alt=&quot;&quot; width=&quot;789&quot; height=&quot;311&quot; class=&quot;alignnone size-full wp-image-2639&quot;&gt;&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;large nothing&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large largesingleimage&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large compositeimage&lt;&#x2F;td&gt;&lt;td&gt;80&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;offscreen-images&quot;&gt;Offscreen images&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;offscreen-results.png&quot; alt=&quot;&quot; width=&quot;821&quot; height=&quot;312&quot; class=&quot;alignnone size-full wp-image-2640&quot;&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;large nothing&lt;&#x2F;td&gt;&lt;td&gt;93&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;80&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 largeimages offscreenimages&lt;&#x2F;td&gt;&lt;td&gt;83&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;feedback-please&quot;&gt;Feedback please&lt;&#x2F;h2&gt;
&lt;p&gt;Please do get back to me with tips about how to improve the performance of my experimental code.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to log &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;issues&quot;&gt;issues&lt;&#x2F;a&gt;, make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;merge_requests&quot;&gt;merge requests&lt;&#x2F;a&gt; or add comments to the blog post.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Performance of Java 2D drawing operations (part 1: types of operation)</title>
          <pubDate>Mon, 04 Feb 2019 03:21:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/02/04/performance-of-java-2d-drawing-operations/</link>
          <guid>https://artificialworlds.net/blog/2019/02/04/performance-of-java-2d-drawing-operations/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/02/04/performance-of-java-2d-drawing-operations/">&lt;p&gt;Series: operations, &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;08&#x2F;performance-of-java-2d-drawing-operations-part-2-image-issues&#x2F;&quot;&gt;images&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;11&#x2F;performance-of-java-2d-drawing-operations-part-3-image-opacity&#x2F;&quot;&gt;opacity&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I want to remodel the desktop UI of my game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; to be more convenient and nicer looking, so I took a new look at game-loop-style graphics rendering onto a canvas in a Java 2D (Swing) UI.&lt;&#x2F;p&gt;
&lt;p&gt;For more on images, see the &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;08&#x2F;performance-of-java-2d-drawing-operations-part-2-image-issues&#x2F;&quot;&gt;next post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Specifically, how fast can it be, and what pitfalls should I avoid when I&#x27;m doing it?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Larger windows are (much) slower&lt;&#x2F;li&gt;
&lt;li&gt;Resizing images on-the-fly is very slow, &lt;strong&gt;even if they are the same size every time&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Drawing small images is fast, but drawing large images is slow&lt;&#x2F;li&gt;
&lt;li&gt;Drawing rectangles is fast&lt;&#x2F;li&gt;
&lt;li&gt;Drawing text is fast&lt;&#x2F;li&gt;
&lt;li&gt;Drawing Swing widgets in front of a canvas is fast&lt;&#x2F;li&gt;
&lt;li&gt;Creating fonts on-the-fly is a tiny bit slow&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;code&quot;&gt;Code&lt;&#x2F;h2&gt;
&lt;p&gt;You can find the full code (written in Kotlin) at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&quot;&gt;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Basically, we make a JFrame and a Canvas and tell them not to listen to repaints (i.e. we control their drawing).&lt;&#x2F;p&gt;
&lt;pre&gt;val app = JFrame()
app.ignoreRepaint = true
val canvas = Canvas()
canvas.ignoreRepaint = true&lt;&#x2F;pre&gt;
&lt;p&gt;Then we add any buttons to the JFrame, and the canvas last (so it displays behind):&lt;&#x2F;p&gt;
&lt;pre&gt;app.add(button)
app.add(canvas)&lt;&#x2F;pre&gt;
&lt;p&gt;Now we make the canvas double-buffered and get hold of a buffer image for it:&lt;&#x2F;p&gt;
&lt;pre&gt;app.isVisible = true
canvas.createBufferStrategy(2)
val bufferStrategy = canvas.bufferStrategy
val bufferedImage = GraphicsEnvironment
    .getLocalGraphicsEnvironment()
    .defaultScreenDevice
    .defaultConfiguration
    .createCompatibleImage(config.width, config.height)&lt;&#x2F;pre&gt;
&lt;p&gt;Then inside a tight loop we draw onto the buffer image:&lt;&#x2F;p&gt;
&lt;pre&gt;val g2d = bufferedImage.createGraphics()
try
{
    g2d.color = backgroundColor
    g2d.fillRect(0, 0, config.width, config.height)

    ... the different drawing operations go here ...&lt;&#x2F;pre&gt;
&lt;p&gt;and then swap the buffers:&lt;&#x2F;p&gt;
&lt;pre&gt;    val graphics = bufferStrategy.drawGraphics
    try {
        graphics.drawImage(bufferedImage, 0, 0, null)
        if (!bufferStrategy.contentsLost()) {
            bufferStrategy.show()
        }
    } finally {
        graphics.dispose()
    }
} finally {
    g2d.dispose()
}&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;results-1&quot;&gt;Results&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;baseline-some-rectangles&quot;&gt;Baseline: some rectangles&lt;&#x2F;h3&gt;
&lt;p&gt;I decided to compare everything against drawing 20 rectangles at random points on the screen, since that seems like a minimal requirement for a game.&lt;&#x2F;p&gt;
&lt;p&gt;My test machine is an Intel Core 2 Duo E6550 2.33GHz with 6GB RAM and a GeForce GT 740 graphics card (I have no idea whether it is being used here - I assume not). I am running Ubuntu 18.04.1 Linux, OpenJDK Java 1.8.0_191, and Kotlin 1.3.20-release-116. (I expect the results would be identical if I were using Java rather than Kotlin.)&lt;&#x2F;p&gt;
&lt;p&gt;I ran all the tests in two window sizes: 1600x900 and 640x480. 640x480 was embarrassingly fast for all tests, but 1600x900 struggled with some of the tasks.&lt;&#x2F;p&gt;
&lt;p&gt;Drawing rectangles looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;g2d.color = Color(
    rand.nextInt(256),
    rand.nextInt(256),
    rand.nextInt(256)
)
g2d.fillRect(
    rand.nextInt(config.width &#x2F; 2),
    rand.nextInt(config.height &#x2F; 2),
    rand.nextInt(config.width &#x2F; 2),
    rand.nextInt(config.height &#x2F; 2)
)&lt;&#x2F;pre&gt;
&lt;p&gt;In the small window, the baseline (20 rectangles) ran at 553 FPS. In the large window it ran at 87 FPS.&lt;&#x2F;p&gt;
&lt;p&gt;I didn&#x27;t do any statistics on these numbers because I am too lazy. Feel free to do it properly and let me know the results - I will happily update the article.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;fewer-rectangles&quot;&gt;Fewer rectangles&lt;&#x2F;h3&gt;
&lt;p&gt;When I reduced the number of rectangles to do less drawing work, I saw small improvements in performance. In the small window, drawing 2 rectangles instead of 20 increased the frame rate from 553 to 639, but there is a lot of noise in those results, and other runs were much closer. In the large window, the same reduction improved the frame rate from 87 to 92. This is not a huge speed-up, showing that drawing rectangles is pretty fast.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-fixed-size-images&quot;&gt;Adding fixed-size images&lt;&#x2F;h3&gt;
&lt;p&gt;Drawing pre-scaled images looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;g2d.drawImage(
    image,
    rand.nextInt(config.width),
    rand.nextInt(config.height),
    null
)&lt;&#x2F;pre&gt;
&lt;p&gt;When I added 20 small images (40x40 pixels) to be drawn in each frame, the performance was almost unchanged. In the small window, the run showing 20 images per frame (as well as rectangle) actually ran faster than the one without (561 FPS versus 553), suggesting the difference is negligible and I should do some statistics. In the large window, the 20 images version ran at exactly the same speed (87 FPS).&lt;&#x2F;p&gt;
&lt;p&gt;So, it looks like drawing small images costs almost nothing.&lt;&#x2F;p&gt;
&lt;p&gt;When I moved to large images (400x400 pixels), the small window slowed down from 553 to 446 FPS, and the large window slowed from 87 to 73 FPS, so larger images clearly have an impact, and we will need to limit the number and size of images to keep the frame rate acceptable.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;scaling-images-on-the-fly&quot;&gt;Scaling images on the fly&lt;&#x2F;h3&gt;
&lt;p&gt;You can scale an image on the fly as you draw onto a Canvas. (Spoiler: don&#x27;t do this!)&lt;&#x2F;p&gt;
&lt;p&gt;My code looks like:&lt;&#x2F;p&gt;
&lt;pre&gt;val s = config.imageSize
val x1 = rand.nextInt(config.width)
val y1 = rand.nextInt(config.height)
val x2 = x1 + s
val y2 = y1 + s
g2d.drawImage(
    unscaledImage,
    x1, y1, x2, y2,
    0, 0, unscaledImageWidth, unscaledImageHeight,
    null
)&lt;&#x2F;pre&gt;
&lt;p&gt;Note the 10-argument form of drawImage is being used. You can be sure you have avoided this situation if you use the 4-argument form from the previous section.&lt;&#x2F;p&gt;
&lt;p&gt;Note: the resulting image is &lt;strong&gt;the same size every time&lt;&#x2F;strong&gt;, and the Java documentation implies that scaled images may be cached by the system, but I saw a &lt;strong&gt;huge slow-down&lt;&#x2F;strong&gt; when using the 10-argument form of drawImage above.&lt;&#x2F;p&gt;
&lt;p&gt;On-the-fly scaled images slowed the small window from 446 to 67 FPS(!), and the large window from 73 to 31 FPS, meaning the exact same rendering took over twice as long.&lt;&#x2F;p&gt;
&lt;p&gt;Advice: check you are not using one of the drawImage overloads that scales images! Pre-scale them yourself (e.g. with getScaledInstance as I did here).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;displaying-text&quot;&gt;Displaying text&lt;&#x2F;h3&gt;
&lt;p&gt;Drawing text on the canvas like this:&lt;&#x2F;p&gt;
&lt;pre&gt;g2d.font = Font(&quot;Courier New&quot;, Font.PLAIN, 12)
g2d.color = Color.GREEN
g2d.drawString(&quot;FPS: $fpsLastSecond&quot;, 20, 20 + i * 14)&lt;&#x2F;pre&gt;
&lt;p&gt;had a similar impact to drawing small images - i.e. it only affected the performance very slightly and is generally quite fast. The small window slowed from 553 to 581 FPS, and the large window from 87 to 88.&lt;&#x2F;p&gt;
&lt;p&gt;Creating the font every time (as shown above) slowed the process a little more, so it is worth moving the font creation out of the game loop and only doing it once. The slowdown just for creating the font was 581 to 572 FPS in the small window, and 88 to 86 FPS in the large.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;swing-widgets&quot;&gt;Swing widgets&lt;&#x2F;h3&gt;
&lt;p&gt;By adding Button widgets to the JFrame before the Canvas, I was able to display them in front. Their rendering and focus worked as expected, and they had no impact at all on performance.&lt;&#x2F;p&gt;
&lt;p&gt;The same was true when I tried adding these widgets in front of images rendered on the canvas (instead of rectangles).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turning-everything-up-to-11&quot;&gt;Turning everything up to 11&lt;&#x2F;h3&gt;
&lt;p&gt;When I added everything I had tested all at the same time: rectangles, text with a new font every time, large unscaled images, and large window, the frame rate reduced to 30 FPS. This is a little slow for a game already, and if we had more images to draw it could get even worse. However, when I pre-scaled the images the frame rate went up to 72 FPS, showing that Java is capable of running a game at an acceptable frame rate on my machine, so long as we are careful how we use it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;numbers&quot;&gt;Numbers&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;small-window-640x480&quot;&gt;Small window (640x480)&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;small-results.png&quot; alt=&quot;&quot; width=&quot;744&quot; height=&quot;311&quot; class=&quot;alignnone size-full wp-image-2627&quot;&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;nothing&lt;&#x2F;td&gt;&lt;td&gt;661&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles2&lt;&#x2F;td&gt;&lt;td&gt;639&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20&lt;&#x2F;td&gt;&lt;td&gt;553&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 images2&lt;&#x2F;td&gt;&lt;td&gt;538&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 images20&lt;&#x2F;td&gt;&lt;td&gt;561&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 images20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;446&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 images20 unscaledimages&lt;&#x2F;td&gt;&lt;td&gt;343&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 images20 largeimages unscaledimages&lt;&#x2F;td&gt;&lt;td&gt;67&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 text2&lt;&#x2F;td&gt;&lt;td&gt;582&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 text20&lt;&#x2F;td&gt;&lt;td&gt;581&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 text20 newfont&lt;&#x2F;td&gt;&lt;td&gt;572&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 buttons2&lt;&#x2F;td&gt;&lt;td&gt;598&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;rectangles20 buttons20&lt;&#x2F;td&gt;&lt;td&gt;612&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;large-window-1200x900&quot;&gt;Large window (1200x900)&lt;&#x2F;h3&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;large-results.png&quot; alt=&quot;&quot; width=&quot;819&quot; height=&quot;316&quot; class=&quot;alignnone size-full wp-image-2626&quot;&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test&lt;&#x2F;th&gt;&lt;th&gt;FPS&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;large nothing&lt;&#x2F;td&gt;&lt;td&gt;93&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles2&lt;&#x2F;td&gt;&lt;td&gt;92&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images2&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;73&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20 unscaledimages&lt;&#x2F;td&gt;&lt;td&gt;82&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20 largeimages unscaledimages&lt;&#x2F;td&gt;&lt;td&gt;31&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 text2&lt;&#x2F;td&gt;&lt;td&gt;89&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 text20&lt;&#x2F;td&gt;&lt;td&gt;88&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 text20 newfont&lt;&#x2F;td&gt;&lt;td&gt;86&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 buttons2&lt;&#x2F;td&gt;&lt;td&gt;88&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 buttons20&lt;&#x2F;td&gt;&lt;td&gt;87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large images20 buttons20 largeimages&lt;&#x2F;td&gt;&lt;td&gt;74&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20 text20 buttons20 largeimages newfont&lt;&#x2F;td&gt;&lt;td&gt;72&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;large rectangles20 images20 text20 buttons20 largeimages unscaledimages newfont&lt;&#x2F;td&gt;&lt;td&gt;30&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;feedback-please&quot;&gt;Feedback please&lt;&#x2F;h2&gt;
&lt;p&gt;Please do get back to me with tips about how to improve the performance of my experimental code.&lt;&#x2F;p&gt;
&lt;p&gt;Feel free to log &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;issues&quot;&gt;issues&lt;&#x2F;a&gt;, make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;java-2d-performance&#x2F;merge_requests&quot;&gt;merge requests&lt;&#x2F;a&gt; or add comments to the blog post.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>London Python Meetup January 2019 - Async Python and GeoPandas</title>
          <pubDate>Thu, 24 Jan 2019 14:14:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/01/24/london-python-meetup-january-2019-async-python-and-geopandas/</link>
          <guid>https://artificialworlds.net/blog/2019/01/24/london-python-meetup-january-2019-async-python-and-geopandas/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/01/24/london-python-meetup-january-2019-async-python-and-geopandas/">&lt;p&gt;It was a pleasure to go to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.meetup.com&#x2F;LondonPython&#x2F;&quot;&gt;London Python Meetup&lt;&#x2F;a&gt; organised by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;python_london&quot;&gt;@python_london&lt;&#x2F;a&gt;. There were plenty of friendly people and interesting conversations.&lt;&#x2F;p&gt;
&lt;p&gt;I gave a talk &quot;Making 100 million requests with Python aiohttp&quot; (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;python-async&#x2F;python-async.html&quot;&gt;slides&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;Blog post&lt;&#x2F;a&gt;) explaining the basics of writing async code in Python 3 and how I used that to make a very large number of HTTP requests.&lt;&#x2F;p&gt;
&lt;img src=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;andyb-python-async2.jpeg&quot; alt=&quot;Andy giving the presentation&quot; width=&quot;421&quot; height=&quot;644&quot; class=&quot;alignnone size-full wp-image-2619&quot;&gt;
&lt;p&gt;(Photo by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashpling&quot;&gt;CB Bailey&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully it was helpful - there were several good questions, so I am optimistic that people were engaged with it.&lt;&#x2F;p&gt;
&lt;p&gt;After that, there was an excellent talk by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gareth-lloyd&quot;&gt;Gareth Lloyd&lt;&#x2F;a&gt; called &quot;GeoPandas, the geospatial extension for Pandas&quot; in which he explained how to use the very well-developed geo-spatial data tools available in the Python ecosphere to transform, combine, plot and analyse data which includes location information. I was really impressed with how easy the libraries looked to use, and also with the cool &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jupyter.org&#x2F;&quot;&gt;Jupyter&lt;&#x2F;a&gt; notebook Gareth used to explain the ideas using live demos.&lt;&#x2F;p&gt;
&lt;p&gt;London Python Meetups seem like a cool place to meet Pythonistas of all levels of experience in a nice, low-pressure environment!&lt;&#x2F;p&gt;
&lt;p&gt;Meetup link: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.meetup.com&#x2F;LondonPython&#x2F;events&#x2F;257930118&#x2F;&quot;&gt;aiohttp &#x2F; GeoPandas&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Run bash inside any version of Linux using Docker</title>
          <pubDate>Tue, 08 Jan 2019 10:25:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/01/08/run-bash-inside-any-version-of-linux-using-docker/</link>
          <guid>https://artificialworlds.net/blog/2019/01/08/run-bash-inside-any-version-of-linux-using-docker/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/01/08/run-bash-inside-any-version-of-linux-using-docker/">&lt;p&gt;Docker is useful for some things, and not as useful as you think for others.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s something massively useful: get a throwaway bash prompt inside any version of any Linux distribution in one command:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run -i -t --mount &quot;type=bind,src=$HOME&#x2F;Desktop,dst=&#x2F;Desktop&quot; ubuntu:18.10 bash&lt;&#x2F;pre&gt;
&lt;p&gt;This command downloads a recent Ubuntu 18.10 image, mounts my desktop as &#x2F;Desktop in the container, and gives me a bash prompt. From here I can install any packages I want and then use them.&lt;&#x2F;p&gt;
&lt;p&gt;For example, today I used it to decrypt a file that was encrypted with a cipher my main OS did not have a package for.&lt;&#x2F;p&gt;
&lt;p&gt;When I exit bash, the container stops and I can find it with docker ps -a then remove it with docker rm. To really clean up I can find the downloaded images with docker image ls and remove them with docker image rm.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Windows missing from grub menu after Ubuntu install</title>
          <pubDate>Thu, 03 Jan 2019 20:47:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2019/01/03/windows-missing-from-grub-menu-after-ubuntu-install/</link>
          <guid>https://artificialworlds.net/blog/2019/01/03/windows-missing-from-grub-menu-after-ubuntu-install/</guid>
          <description xml:base="https://artificialworlds.net/blog/2019/01/03/windows-missing-from-grub-menu-after-ubuntu-install/">&lt;p&gt;Windows was missing from my grub menu after I installed Ubuntu. Fortunately it was fixed by running:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo grub-mkconfig -o &#x2F;boot&#x2F;grub&#x2F;grub.cfg&lt;&#x2F;pre&gt;
&lt;p&gt;and rebooting.&lt;&#x2F;p&gt;
&lt;p&gt;Phew.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New home page design</title>
          <pubDate>Sat, 22 Dec 2018 00:04:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/12/22/new-home-page-design/</link>
          <guid>https://artificialworlds.net/blog/2018/12/22/new-home-page-design/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/12/22/new-home-page-design/">&lt;p&gt;After years of getting around to it, I have redesigned my home page at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&quot;&gt;artificialworlds.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s basically intended to make me look clever or productive or interesting or something. Alternatively, it gives you somewhere to find that thing you know I made but can&#x27;t find the link:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;andybalaamshome.png&quot; alt=&quot;Screenshot of artificialworlds.net - colourful boxes with rich-coloured picture of drawers behind.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The background image is &quot;Read where you are&quot; by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;unsplash.com&#x2F;@deltabiaram&quot;&gt;delaram bayat.&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I am pleased with the page&#x27;s responsive design, clarity, fast page-load, and colourfulness.&lt;&#x2F;p&gt;
&lt;p&gt;What do you think?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Code Like a Girl T-shirts</title>
          <pubDate>Mon, 17 Dec 2018 23:15:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/12/17/code-like-a-girl-t-shirts/</link>
          <guid>https://artificialworlds.net/blog/2018/12/17/code-like-a-girl-t-shirts/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/12/17/code-like-a-girl-t-shirts/">&lt;p&gt;There are lots of people missing from the programming world: lots of the programmers I meet look and sound a lot like me. I&#x27;d really like it if this amazing job were open to a lot more people.&lt;&#x2F;p&gt;
&lt;p&gt;One of the weird things that has happened is that somehow we seem to have the idea that programming is only for boys, and I&#x27;d like to fight against that idea by wearing a t-shirt demonstrating how cool I think it is to be a woman coder.&lt;&#x2F;p&gt;
&lt;p&gt;So, I commissioned a design from an amazing artist called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ellie-mars.myportfolio.com&#x2F;&quot;&gt;Ellie Mars&lt;&#x2F;a&gt;, who I found through her Mastodon.art page &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.art&#x2F;@elliemars&quot;&gt;@elliemars@mastodon.art&lt;&#x2F;a&gt;. She did an amazing job, sending sketches and ideas back and forth, and finally she came up with this awesome design:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;codelikeagirl-blackbg-signature-542x700.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve printed a t-shirt for myself that I will give myself for Christmas, and I&#x27;ve made a page on Street Shirts so you can get one too!&lt;&#x2F;p&gt;
&lt;p&gt;The link goes to an adult men&#x27;s t-shirt, but after you click the link you can choose the t-shirt type (including women&#x27;s and children&#x27;s) and change the background colour.Â They are reasonably cheap:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.streetshirts.co.uk&#x2F;design-your-own-t-shirts?designkey=PFC%5BSSDco8Fpa1ccjI8oyR0s0jFkpoeJt2%5D&quot;&gt;Code Like a Girl t-shirt &lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;t-shirt-mens-200x230.png&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If you have any questions, you are welcome to contact me (via DM or publicly) on twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;@andybalaam&lt;&#x2F;a&gt; or Mastodon &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;@andybalaam&quot;&gt;@andybalaam@mastodon.social&lt;&#x2F;a&gt;, or by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Main&#x2F;HumanPeople&quot;&gt;email, via a short test&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Ellie and I agreed to set up these t-shirts sales with no profit for us because we&#x27;d like to get the word out.Â If they are popular we might add a little, so get in fast for a good deal!&lt;&#x2F;p&gt;
&lt;p&gt;10 points for anyone who can recognise the code in the background.Â It&#x27;s from one of my favourite programs.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I think we all spend too much of our time walking around advertising faceless corporations when we could be saying something a bit more useful on our clothes.Â What do you think of this idea?Â Maybe you could design a similar t-shirt?Â Let me know your thoughts in the comments below or on Twitter or Mastodon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Poor performance in Chrome (especially on mobile)  - caused by SVG background images</title>
          <pubDate>Mon, 10 Dec 2018 01:08:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/12/10/poor-performance-in-chrome-especially-on-mobile-caused-by-svg-background-images/</link>
          <guid>https://artificialworlds.net/blog/2018/12/10/poor-performance-in-chrome-especially-on-mobile-caused-by-svg-background-images/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/12/10/poor-performance-in-chrome-especially-on-mobile-caused-by-svg-background-images/">&lt;p&gt;I have spent the last few hours investigating abysmal performance in my latest little game project &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;cross-the-road&quot;&gt;Cross The Road&lt;&#x2F;a&gt;. Firefox was fine, but Chromium and Chrome, especially on mobile, was rendering at about three frames per second.&lt;&#x2F;p&gt;
&lt;p&gt;When I stopped using SVGs as background-images for my elements, and used PNGs instead, it improved to about 20-30 FPS.&lt;&#x2F;p&gt;
&lt;p&gt;It seems fine to use SVGs as normal images, but for background-image, it really hurt performance.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Interesting Characters (UTF-16, utf-8, Unicode, encodings) video</title>
          <pubDate>Sat, 24 Nov 2018 09:00:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/24/interesting-characters-utf-16-utf-8-unicode-encodings-video/</link>
          <guid>https://artificialworlds.net/blog/2018/11/24/interesting-characters-utf-16-utf-8-unicode-encodings-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/24/interesting-characters-utf-16-utf-8-unicode-encodings-video/">&lt;p&gt;In which I try to express the weird joy I felt learning about all the odd quirks of the simple task of turning bytes into characters, and characters into bytes.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Interesting Characters (UTF-16, utf-8, Unicode, encodings)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ae6cc8b2-f78c-4711-8188-400d5c714f4b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;character-encodings&#x2F;character-encodings.html&quot;&gt;Interesting Characters slides&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Elm makes me happy (updated for Elm 0.19) video</title>
          <pubDate>Sat, 10 Nov 2018 02:45:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/10/elm-makes-me-happy-updated-for-elm-0-19-video/</link>
          <guid>https://artificialworlds.net/blog/2018/11/10/elm-makes-me-happy-updated-for-elm-0-19-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/10/elm-makes-me-happy-updated-for-elm-0-19-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Snake in Elm&lt;&#x2F;a&gt;, Elm makes me happy, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;05&#x2F;13&#x2F;elm-basics-video&#x2F;&quot;&gt;Elm Basics&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;11&#x2F;01&#x2F;writing-a-unit-test-in-elm&#x2F;&quot;&gt;Elm Unit Test&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;10&#x2F;19&#x2F;elm-json-decoder-examples&#x2F;&quot;&gt;Elm JSON&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With up-to-date examples, and anecdotes from the last couple of years of continuing to enjoy writing web pages in Elm, here&#x27;s a new version of my Elm advert:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Elm makes me happy (updated for Elm 0.19)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;9f32e5bf-cc8c-4f36-adf9-7dae1cb8e6e9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;elm-makes-me-happy&#x2F;elm-makes-me-happy.html&quot;&gt;Elm makes me happy slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Godot: make new objects at runtime (instancing) video</title>
          <pubDate>Sat, 10 Nov 2018 02:28:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/10/godot-make-new-objects-at-runtime-instancing-video/</link>
          <guid>https://artificialworlds.net/blog/2018/11/10/godot-make-new-objects-at-runtime-instancing-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/10/godot-make-new-objects-at-runtime-instancing-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;06&#x2F;godot-2d-shapes-bouncing-off-each-other-video&#x2F;&quot;&gt;2D Shapes&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;08&#x2F;godot-dragging-and-dropping-physics-objects-video&#x2F;&quot;&gt;drag and drop&lt;&#x2F;a&gt;, new objects&lt;&#x2F;p&gt;
&lt;p&gt;My Godot 3 game is progressing, and I am starting to think I am actually writing the level editor. Here&#x27;s how I wrote code to make new versions of existing objects (by converting them to scenes):&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Godot: making new objects at runtime (instancing)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;32cf63d9-71b8-48bf-9bff-b2ac838e22ec&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Godot version: v3.0.6.stable.official.8314054&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>You must rewind your incoming buffer when you fail to encode a character in a CharsetEncoder or you&#x27;ll get an IllegalArgumentException</title>
          <pubDate>Fri, 09 Nov 2018 15:56:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/09/you-must-rewind-your-incoming-buffer-when-you-fail-to-encode-a-character-in-a-charsetencoder-or-youll-get-an-illegalargumentexception/</link>
          <guid>https://artificialworlds.net/blog/2018/11/09/you-must-rewind-your-incoming-buffer-when-you-fail-to-encode-a-character-in-a-charsetencoder-or-youll-get-an-illegalargumentexception/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/09/you-must-rewind-your-incoming-buffer-when-you-fail-to-encode-a-character-in-a-charsetencoder-or-youll-get-an-illegalargumentexception/">&lt;p&gt;I am writing a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;8&#x2F;docs&#x2F;api&#x2F;java&#x2F;nio&#x2F;charset&#x2F;CharsetEncoder.html&quot;&gt;CharsetEncoder&lt;&#x2F;a&gt; in Java, which is my kind of fun.&lt;&#x2F;p&gt;
&lt;p&gt;I was getting a mysterious error when I identified that I could not encode certain characters:&lt;&#x2F;p&gt;
&lt;pre&gt;Exception in thread &quot;main&quot; java.lang.IllegalArgumentException
	at java.nio.Buffer.position(Buffer.java:244)
	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:618)
	at java.nio.charset.CharsetEncoder.encode(CharsetEncoder.java:802)
	at java.nio.charset.Charset.encode(Charset.java:843)
	at java.nio.charset.Charset.encode(Charset.java:863)&lt;&#x2F;pre&gt;
&lt;p&gt;After some investigation I realised the library code in Charset.encode was expecting me not to have consumed any characters of my incoming CharBuffer if I rejected the input by returning something like CoderResult.unmappableForLength.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, in order to discover the input was unmappable, I did have to read it, but I made this problem go away by stepping back one char when I found an error like this:&lt;&#x2F;p&gt;
&lt;pre&gt;@Override
public CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
    char ch = in.get();
    if(isUnmappable(ch)) {
        &lt;span style=&quot;color: red;&quot;&gt;in.position(in.position() - 1);&lt;&#x2F;span&gt;
        return CoderResult.unmappableForLength(2);
    }
    &#x2F;&#x2F; ... rest of method ...&lt;&#x2F;pre&gt;
&lt;p&gt;I hope this helps someone.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Godot: Dragging and dropping physics objects video</title>
          <pubDate>Thu, 08 Nov 2018 01:37:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/08/godot-dragging-and-dropping-physics-objects-video/</link>
          <guid>https://artificialworlds.net/blog/2018/11/08/godot-dragging-and-dropping-physics-objects-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/08/godot-dragging-and-dropping-physics-objects-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;06&#x2F;godot-2d-shapes-bouncing-off-each-other-video&#x2F;&quot;&gt;2D Shapes&lt;&#x2F;a&gt;, drag and drop, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;godot-make-new-objects-at-runtime-instancing-video&#x2F;&quot;&gt;new objects&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Continuing to explore the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;godotengine.org&#x2F;&quot;&gt;Godot 3&lt;&#x2F;a&gt; game engine. I want to make a game where you drag blocks around and balance them on each other, but I couldn&#x27;t find much documentation on how to drag-and-drop objects (except menu UI elements), and especially I found quite a few wrinkles when doing this with objects that are normally controlled by the physics engine.&lt;&#x2F;p&gt;
&lt;p&gt;This time we actually write some code in Godot&#x27;s programming language, GDScript.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Godot: Drag-and-drop of physics objects&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;40766c22-2651-47ab-afe5-17e3428f97b5&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Godot version: v3.0.6.stable.official.8314054&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Godot: 2D shapes bouncing off each other video</title>
          <pubDate>Tue, 06 Nov 2018 21:26:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/11/06/godot-2d-shapes-bouncing-off-each-other-video/</link>
          <guid>https://artificialworlds.net/blog/2018/11/06/godot-2d-shapes-bouncing-off-each-other-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/11/06/godot-2d-shapes-bouncing-off-each-other-video/">&lt;p&gt;Series: 2D Shapes, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;08&#x2F;godot-dragging-and-dropping-physics-objects-video&#x2F;&quot;&gt;drag and drop&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;godot-make-new-objects-at-runtime-instancing-video&#x2F;&quot;&gt;new objects&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In which I make a square and a triangle bounce on a rectangle using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;godotengine.org&#x2F;&quot;&gt;Godot 3&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Godot: 2D shapes bouncing off each other&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;f7b73416-6f9f-4457-baf2-d3ef20ca8457&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Godot version: v3.0.6.stable.official.8314054&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Elm JSON decoder examples</title>
          <pubDate>Fri, 19 Oct 2018 08:53:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/10/19/elm-json-decoder-examples/</link>
          <guid>https://artificialworlds.net/blog/2018/10/19/elm-json-decoder-examples/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/10/19/elm-json-decoder-examples/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Snake in Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;elm-makes-me-happy-updated-for-elm-0-19-video&#x2F;&quot;&gt;Elm makes me happy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;05&#x2F;13&#x2F;elm-basics-video&#x2F;&quot;&gt;Elm Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;11&#x2F;01&#x2F;writing-a-unit-test-in-elm&#x2F;&quot;&gt;Elm Unit Test&lt;&#x2F;a&gt;, Elm JSON&lt;&#x2F;p&gt;
&lt;p&gt;I find &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;effects&#x2F;json.html&quot;&gt;JSON decoding&lt;&#x2F;a&gt; in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; confusing, so here are some thoughts and examples.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;pre&gt;$ elm --version
0.19.0
$ mkdir myproj; cd myproj
$ elm init
...
$ elm install elm&#x2F;json
...
&lt;&#x2F;pre&gt;
&lt;p&gt;To run the &quot;Demo&quot; parts of the examples below, type them into the interactive Elm interpreter. To try them out, start it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ elm repl&lt;&#x2F;pre&gt;
&lt;p&gt;and import the library you need:&lt;&#x2F;p&gt;
&lt;pre&gt;import Json.Decode as D&lt;&#x2F;pre&gt;
&lt;p&gt;Scroll to &quot;Concepts&quot; at the bottom for lots of waffling about what is really going on, but if you&#x27;re looking to copy and paste concrete examples, here we are:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;json-object-to-record&quot;&gt;JSON object to Record&lt;&#x2F;h3&gt;
&lt;pre style=&quot;font-weight: bold;&quot;&gt;type alias MyRecord =
    { i : Int
    , s : String
    }

recordDecoder : D.Decoder MyRecord
recordDecoder =
    D.map2
        MyRecord
        (D.field &quot;i&quot; D.int)
        (D.field &quot;s&quot; D.string)&lt;&#x2F;pre&gt;
&lt;p&gt;Note: this uses a special trick - it turns out you can construct a record like MyRecord writing e.g. MyRecord 3 &quot;qux&quot; - the arguments have to be in the same order that the properties are declared in the record. The above only works because you can do this.&lt;&#x2F;p&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; type alias MyRec = {i: Int, s: String}
&amp;gt; myRecDec = D.map2 MyRec (D.field &quot;i&quot; D.int) (D.field &quot;s&quot; D.string)
&amp;lt;internals&amp;gt; : D.Decoder MyRec
&amp;gt; D.decodeString myRecDec &quot;{\&quot;i\&quot;: 3, \&quot;s\&quot;: \&quot;bar\&quot;}&quot;
Ok { i = 3, s = &quot;bar&quot; }
    : Result D.Error MyRec
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;json-array-of-ints-to-list&quot;&gt;JSON array of ints to List&lt;&#x2F;h3&gt;
&lt;pre style=&quot;font-weight: bold;&quot;&gt;intArrayDecoder : D.Decoder (List Int)
intArrayDecoder =
    D.list D.int&lt;&#x2F;pre&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; myArrDec = D.list D.int
&amp;lt;internals&amp;gt; : D.Decoder (List Int)
&amp;gt; D.decodeString myArrDec &quot;[3, 4]&quot;
Ok [3,4] : Result D.Error (List Int)
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;json-array-of-strings-to-list&quot;&gt;JSON array of strings to List&lt;&#x2F;h3&gt;
&lt;pre style=&quot;font-weight: bold;&quot;&gt;stringArrayDecoder : D.Decoder (List String)
stringArrayDecoder =
    D.list D.string&lt;&#x2F;pre&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; myArrDec2 = D.list D.string
&amp;lt;internals&amp;gt; : D.Decoder (List String)
&amp;gt; D.decodeString myArrDec2 &quot;[\&quot;a\&quot;, \&quot;b\&quot;]&quot;
Ok [&quot;a&quot;,&quot;b&quot;] : Result D.Error (List String)&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;json-object-to-dict&quot;&gt;JSON object to Dict&lt;&#x2F;h3&gt;
&lt;pre style=&quot;font-weight: bold;&quot;&gt;intDictDecoder : D.Decoder (Dict String Int)
intDictDecoder =
    D.dict D.int&lt;&#x2F;pre&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; myDictDecoder = D.dict D.int
&amp;lt;internals&amp;gt; : D.Decoder (Dict.Dict String Int)
&amp;gt; D.decodeString myDictDecoder &quot;{\&quot;a\&quot;: \&quot;b\&quot;}&quot;
Err (Field &quot;a&quot; (Failure (&quot;Expecting an INT&quot;) &amp;lt;internals&amp;gt;))
    : Result D.Error (Dict.Dict String Int)
&amp;gt; D.decodeString myDictDecoder &quot;{\&quot;a\&quot;: 3}&quot;
Ok (Dict.fromList [(&quot;a&quot;,3)])
    : Result D.Error (Dict.Dict String Int)&lt;&#x2F;pre&gt;
&lt;p&gt;To build a Dict of String to String, replace D.int above withD.string.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;json-array-of-objects-to-list-of-records&quot;&gt;JSON array of objects to List of Records&lt;&#x2F;h3&gt;
&lt;pre style=&quot;font-weight: bold;&quot;&gt;type alias MyRecord =
    { i : Int
    , s : String
    }

recordDecoder : D.Decoder MyRecord
recordDecoder =
    D.map2
        MyRecord
        (D.field &quot;i&quot; D.int)
        (D.field &quot;s&quot; D.string)

listOfRecordsDecoder : D.Decoder (List MyRecord)
listOfRecordsDecoder =
    D.list recordDecoder&lt;&#x2F;pre&gt;
&lt;p&gt;Demo:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; import Json.Decode as D
&amp;gt; type alias MyRec = {i: Int, s: String}
&amp;gt; myRecDec = D.map2 MyRec (D.field &quot;i&quot; D.int) (D.field &quot;s&quot; D.string)
&amp;lt;internals&amp;gt; : D.Decoder MyRec
&amp;gt; listOfMyRecDec = D.list myRecDec
&amp;lt;internals&amp;gt; : D.Decoder (List MyRec)
&amp;gt; D.decodeString listOfMyRecDec &quot;[{\&quot;i\&quot;: 4, \&quot;s\&quot;: \&quot;one\&quot;}, {\&quot;i\&quot;: 5, \&quot;s\&quot;:\&quot;two\&quot;}]&quot;
Ok [{ i = 4, s = &quot;one&quot; },{ i = 5, s = &quot;two&quot; }]
    : Result D.Error (List MyRec)&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;concepts&quot;&gt;Concepts&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;what-is-a-decoder&quot;&gt;What is a Decoder?&lt;&#x2F;h3&gt;
&lt;p&gt;A Decoder is something that describes how to take in JSON and spit out something. The &quot;something&quot; part is written after Decoder, so e.g. Decoder Int describes how to take in JSON and spit out an Int.&lt;&#x2F;p&gt;
&lt;p&gt;The Json.Decode module contains a function that is a Decoder Int. It&#x27;s called int:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.int
&amp;lt;internals&amp;gt; : D.Decoder Int&lt;&#x2F;pre&gt;
&lt;p&gt;In some not-all-all-true way, a Decoder is sort of like a function:&lt;pre&gt;-- This is a lie, but just pretend with me for a sec
Decoder a : SomeJSON -&amp;gt; a
-- That was a lie
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To actually run your a Decoder, provide it to a function like decodeString:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeString D.int &quot;45&quot;
Ok 45 : Result D.Error Int&lt;&#x2F;pre&gt;
&lt;p&gt;So the actually-true way of getting an actual function is to combine decodeString and a decoder like int:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeString D.int
&amp;lt;function&amp;gt; : String -&amp;gt; Result D.Error Int
&lt;&#x2F;pre&gt;
&lt;p&gt;When you apply decodeString to int you get a function that takes in a String and returns either an Int or an error. The error could be because the string you passed was not valid JSON:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeString D.int &quot;foo bar&quot;
Err (Failure (&quot;This is not valid JSON! Unexpected token o in JSON at position 1&quot;) &lt;internals&gt;)
    : Result D.Error Int&lt;&#x2F;internals&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;or because the parsed JSON does not match what the Decoder you supplied expects:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeString D.int &quot;\&quot;45\&quot;&quot;
Err (Failure (&quot;Expecting an INT&quot;) &lt;internals&gt;)
    : Result D.Error Int&lt;&#x2F;internals&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(We supplied a String containing a JSON string, but the int Decoder expects to find a JSON int.)&lt;&#x2F;p&gt;
&lt;p&gt;Side note: ints and floats are treated as different, even though the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;json.org&quot;&gt;JSON Spec&lt;&#x2F;a&gt; treats them all as just &quot;Numbers&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeString D.int &quot;45.2&quot;
Err (Failure (&quot;Expecting an INT&quot;) &lt;internals&gt;)
    : Result D.Error Int&lt;&#x2F;internals&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;what-is-a-value&quot;&gt;What is a Value?&lt;&#x2F;h3&gt;
&lt;p&gt;Elm has a type that represents JSON that has been parsed (actually, parsed and stored in a JavaScript object) but not interpreted into a useful Elm type. You can make one using the functions inside Json.Encode:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; import Json.Encode as E
&amp;gt; foo = E.string &quot;foo&quot;
&lt;internals&gt; : E.Value
&lt;&#x2F;internals&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can even turn one of these into a String containing JSON using encode:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; E.encode 0 foo
&quot;\&quot;foo\&quot;&quot; : String&lt;&#x2F;pre&gt;
&lt;p&gt;or interpret the Value as useful Elm types using decodeValue:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; D.decodeValue D.string foo
Ok &quot;foo&quot; : Result D.Error String&lt;&#x2F;pre&gt;
&lt;p&gt;(When JSON values come from JavaScript, e.g. via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;interop&#x2F;flags.html&quot;&gt;flags&lt;&#x2F;a&gt;, they actually come as Values, but you don&#x27;t usually need to worry about that.)&lt;&#x2F;p&gt;
&lt;p&gt;However, what you can&#x27;t do is pull Values apart in any way, other than the standard ways Elm gives you. So any custom Decoder that you write has to be built out of existing Decoders.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-do-i-write-my-own-decoder&quot;&gt;How do I write my own Decoder?&lt;&#x2F;h3&gt;
&lt;p&gt;If you want to make a Decoder that does custom things, build it from the existing magic Decoders, give it a type that describes the type it outputs, and insert your code using one of the mapN functions.&lt;&#x2F;p&gt;
&lt;p&gt;For example, to decode only ints that are below 100:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; under100 i = if i &amp;lt; 100 then D.succeed i else (D.fail &quot;Not under 100&quot;)
&amp;lt;function&amp;gt; : number -&amp;gt; D.Decoder number
&amp;gt; intUnder100 = D.int |&amp;gt; D.andThen under100
&amp;lt;internals&amp;gt; : D.Decoder Int
&amp;gt; D.decodeString intUnder100 &quot;50&quot;
Ok 50 : Result D.Error Int
&amp;gt; D.decodeString intUnder100 &quot;500&quot;
Err (Failure (&quot;Not under 100&quot;) &amp;lt;internals&amp;gt;)
    : Result D.Error Int&lt;&#x2F;pre&gt;
&lt;p&gt;Here, we use the andThen function to transform the Int value coming from calling the int function into a Decoder Int that expresses success or failure in terms of decoding. When we do actual decoding using the decodeString funcion, this is transformed into the more familiar Result values like Ok or Err.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to understand the above, pay close attention to the types of under100 and intUnder100.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to write a Decoder that returns some complex type, you should build it using the mapN functions.&lt;&#x2F;p&gt;
&lt;p&gt;For example, to decode strings into arrays of words split by spaces:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; splitIntoWords = String.split &quot; &quot;
&amp;lt;function&amp;gt; : String -&amp;gt; List String
&amp;gt; words = D.map splitIntoWords D.string
&amp;lt;internals&amp;gt; : D.Decoder (List String)
&amp;gt; D.decodeString words &quot;\&quot;foo bar baz\&quot;&quot;
Ok [&quot;foo&quot;,&quot;bar&quot;,&quot;baz&quot;]
    : Result D.Error (List String)
&lt;&#x2F;pre&gt;
&lt;p&gt;Above we used map to transform a Decoder String (the provided string function) into a Decoder (List String) by mapping it over a function (splitIntoWords) that transforms a String into a List String.&lt;&#x2F;p&gt;
&lt;p&gt;Again, to understand this, look carefully at the types of splitIntoWordsand words.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-do-i-build-up-complex-decoders&quot;&gt;How do I build up complex Decoders?&lt;&#x2F;h3&gt;
&lt;p&gt;Complex decoders are built by combining simple ones. Many functions that make decoders take another decoder as an argument. A good example is &quot;JSON array of objects to List of Records&quot; above - there we make a Decoder MyRecord and use it to decode a whole list of records by passing it as an argument to list, so that it returns a Decoder (List MyRecord) which can take in a JSON array of JSON objects, and return a List of MyRecords.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-this-so-confusing&quot;&gt;Why is this so confusing?&lt;&#x2F;h2&gt;
&lt;p&gt;Because Decoders are not functions, but they feel like functions. In fact they are opaque descriptions of how to interpret JSON that the Elm runtime uses to make Elm objects for you out of Values, which are opaque objects that underneath represent a piece of parsed JSON.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Graft Animation Language on Raspberry Pi</title>
          <pubDate>Tue, 09 Oct 2018 23:44:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/10/09/graft-animation-language-on-raspberry-pi/</link>
          <guid>https://artificialworlds.net/blog/2018/10/09/graft-animation-language-on-raspberry-pi/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/10/09/graft-animation-language-on-raspberry-pi/">&lt;p&gt;Because the Rapsberry Pi uses a slightly older Python version, there is a special version of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft&quot;&gt;Graft&lt;&#x2F;a&gt; for it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how to get it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Open a terminal window by clicking the black icon with a &quot;&amp;gt;&quot; symbol on it at the top near the left.&lt;&#x2F;li&gt;
&lt;li&gt;First we need to install a couple of things Graft needs, so type this, then press Enter:&lt;pre&gt;sudo apt install python3-attr at-spi2-core&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;If you want to be able to make animated GIFs, install one more thing:&lt;pre&gt;sudo apt install imagemagick&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;To download Graft and switch to the Raspberry Pi version, type in these commands, pressing Enter after each line.&lt;pre&gt;git clone https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft.git
cd graft
git checkout raspberry-pi&lt;&#x2F;pre&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Now, you should be able to run Graft just like on another computer, for example, like this:&lt;pre&gt;.&#x2F;graft &#x27;d+=10 S()&#x27;&lt;&#x2F;pre&gt; If you&#x27;re looking for a fun way to start, why not try the worksheet &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2018&#x2F;09&#x2F;24&#x2F;worksheet-tell-a-story-by-making-animations-with-code&#x2F;&quot;&gt;&quot;Tell a story by making animations with code&quot;&lt;&#x2F;a&gt;? For more info, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft&#x2F;src&#x2F;branch&#x2F;main&#x2F;raspberrypi.md&quot;&gt;Graft Raspberry Pi Setup&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Worksheet: &quot;Tell a story by making animations with code&quot;</title>
          <pubDate>Mon, 24 Sep 2018 00:57:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/24/worksheet-tell-a-story-by-making-animations-with-code/</link>
          <guid>https://artificialworlds.net/blog/2018/09/24/worksheet-tell-a-story-by-making-animations-with-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/24/worksheet-tell-a-story-by-making-animations-with-code/">&lt;p&gt;I&#x27;m running a workshop at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;EghamJam&quot;&gt;Egham Raspberry Jam&lt;&#x2F;a&gt; on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eventbrite.co.uk&#x2F;e&#x2F;20th-egham-raspberry-jam-tickets-49284593546&quot;&gt;21st October&lt;&#x2F;a&gt;. The workshop will introduce my little animation language &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft&quot;&gt;Graft&lt;&#x2F;a&gt;. We will tell a story using animations that we created ourselves using code.&lt;&#x2F;p&gt;
&lt;p&gt;The worksheet for the workshop is here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations.pdf&quot;&gt;PDF&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations.odp&quot;&gt;ODP&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I reproduced it as images below.&lt;&#x2F;p&gt;
&lt;p&gt;Any feedback much appreciated.&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations_1.png&quot; alt=&quot;&quot; width=&quot;752&quot; height=&quot;1064&quot; class=&quot;alignnone size-full wp-image-2484&quot;&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations_2.png&quot; alt=&quot;&quot; width=&quot;752&quot; height=&quot;1064&quot; class=&quot;alignnone size-full wp-image-2485&quot;&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations_3.png&quot; alt=&quot;&quot; width=&quot;752&quot; height=&quot;1064&quot; class=&quot;alignnone size-full wp-image-2486&quot;&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations_4.png&quot; alt=&quot;&quot; width=&quot;752&quot; height=&quot;1064&quot; class=&quot;alignnone size-full wp-image-2487&quot;&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Tell_a_story_with_animations_5.png&quot; alt=&quot;&quot; width=&quot;752&quot; height=&quot;1064&quot; class=&quot;alignnone size-full wp-image-2488&quot;&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Graft Animation Language on Raspberry Pi – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2018-10-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] If you’re looking for a fun way to start, why not try the worksheet “Tell a story by making animations with code”? […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My experience upgrading to Elm 0.19</title>
          <pubDate>Wed, 12 Sep 2018 01:45:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/12/my-experience-upgrading-to-elm-0-19/</link>
          <guid>https://artificialworlds.net/blog/2018/09/12/my-experience-upgrading-to-elm-0-19/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/12/my-experience-upgrading-to-elm-0-19/">&lt;p&gt;Elm is unstable, so upgrading to the next version can be painful. Here&#x27;s what I needed to do to upgrade from 0.18 to 0.19.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Replace elm-package.json and tests&#x2F;elm-package.json with elm.json - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;e06f5a1728e8948713facfb5a74abc2b7b5b46bd&quot;&gt;e06f5a1728&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Switch to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;libraries.io&#x2F;npm&#x2F;elm-test&#x2F;&quot;&gt;the new elm-test&lt;&#x2F;a&gt; - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;b964b7c7ac97cb315cf6b583484a832f6a8b6c0e&quot;&gt;b964b7c7a&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Re-arrange Main, and how we call it from JavaScript - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;0c118c49fc1913232eb3fdae37b52f3cdaaa8b68&quot;&gt;0c118c49f&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Stop using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;eeue56&#x2F;elm-all-dict&quot;&gt;eeue56&#x2F;elm-all-dict&lt;&#x2F;a&gt; (since it&#x27;s not ported to 0.19 and porting it looked hard due to a lack of Debug.crash) - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;fe100f25612f2a61e80ac1e748a361f95c1b3e64&quot;&gt;fe100f256&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Replace toString with String.fromX or Debug.toString - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;9e78163d0a3442a4aa02259cd7eeecef6303f60d&quot;&gt;9e78163d0a3&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Stop &quot;shadowing&quot; names by making new variables with the same name as another in the scope - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;9688a621dedcf479f01c00b39b7a8ef8204d4c99&quot;&gt;9688a621de&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Adapt to the changed Html.style function - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;b991ab4fa04ca5d143c17f1fa547ae17be624496&quot;&gt;b991ab4f&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Stop using Debug.crash - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;f98a70ad1a6d75ce29872c39724c8d9ef494ec0b&quot;&gt;f98a70ad1&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Adapt to the changes in the Regex module - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;856762a4500df3d5fdfc704e5a3a5a1a694a8321&quot;&gt;856762a4&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Stop using tuples with more than 3 parts - &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;commit&#x2F;472c0bb75d42ed334330af13731ed9e5c36a8ff3&quot;&gt;472c0bb7&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The lack of Debug.crash is really, really painful, especially for a library like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;eeue56&#x2F;elm-all-dict&quot;&gt;eeue56&#x2F;elm-all-dict&lt;&#x2F;a&gt; that has lots of invariants that are hard or impossible to enforce via the type system. On the other hand, if Elm can give a hard guarantee that there will be no runtime errors, this seems pretty cool. The problem is that some code may well have to return the wrong answer silently, instead of crashing, which could be much worse than crashing in some use-cases.&lt;&#x2F;p&gt;
&lt;p&gt;I was annoyed by the lack of more-than-3-part tuples, but even as I did the work to change my code, I saw it get better, so it&#x27;s hard to argue with.&lt;&#x2F;p&gt;
&lt;p&gt;The hardest part to work out was how to run the tests. Fortunately the tests themselves needed almost no changes. I just needed to do this:&lt;&#x2F;p&gt;
&lt;pre&gt;rm -r tests&#x2F;elm-stuff
rm tests&#x2F;elm-package.json
sudo npm install -g elm-test@0.19.0-beta8
elm-test install
elm-test
&lt;&#x2F;pre&gt;
&lt;p&gt;My next job is to check out the --optimize compiler flag, and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;optimization&#x2F;&quot;&gt;advice on making the code smaller and faster&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Bulk adding items to Wunderlist using wunderline on Ubuntu MATE</title>
          <pubDate>Thu, 06 Sep 2018 19:53:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/06/bulk-adding-items-to-wunderlist-using-wunderline-on-ubuntu-mate/</link>
          <guid>https://artificialworlds.net/blog/2018/09/06/bulk-adding-items-to-wunderlist-using-wunderline-on-ubuntu-mate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/06/bulk-adding-items-to-wunderlist-using-wunderline-on-ubuntu-mate/">&lt;p&gt;If you use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wunderlist.com&quot;&gt;Wunderlist&lt;&#x2F;a&gt; and want to be able to bulk-add tasks from a text file, first install and set up &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;wayneashleyberry.github.io&#x2F;wunderline&#x2F;&quot;&gt;wunderline&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now, to be able to right-click a text file containing one task per line on Ubuntu MATE, create a file called &quot;wunderlist-bulk-add&quot; in ~&#x2F;.config&#x2F;caja&#x2F;scripts&#x2F; and make it executable. Paste the code below into that file.&lt;&#x2F;p&gt;
&lt;p&gt;(Note: it&#x27;s possible that this would work in GNOME if you replaced &quot;caja&quot; with &quot;nautius&quot; in that path - let me know in the comments.)&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env bash

set -o pipefail

function nonblank()
{
    grep -v -e &#x27;^[[:space:]]*$&#x27; &quot;$@&quot;
}

for F in &quot;$@&quot;; do
{
    COUNT=$(nonblank &quot;$F&quot; | wc -l | awk &#x27;{print $1}&#x27;)
    if [ &quot;$COUNT&quot; = &quot;&quot; ]; then
    {
        zenity --info --no-wrap --text=&quot;File $F does not exist&quot;
    }
    else
    {
        zenity --question --no-wrap \
            --text=&quot;Add $COUNT items from $F to Wunderlist?&quot;
        ANSWER=$?

        if [ &quot;$ANSWER&quot; = &quot;0&quot; ]; then
        {
            nonblank &quot;$F&quot; | \
                wunderline add --stdin | \
                zenity --progress --text &quot;Adding $COUNT items to Wunderlist&quot;
            SUCCEEDED=$?
            if [ &quot;$SUCCEEDED&quot; = &quot;0&quot; ]; then
            {
                zenity --info --no-wrap --text &quot;Added $COUNT items.&quot;
            }
            else
            {
                zenity --error --no-wrap \
                    --text &quot;Failed to add some or all of these items.&quot;
            }; fi
        }; fi
    }; fi
}; done
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Writing a new Flarum extension on Ubuntu</title>
          <pubDate>Thu, 06 Sep 2018 07:54:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/06/writing-a-new-flarum-extension-on-ubuntu/</link>
          <guid>https://artificialworlds.net/blog/2018/09/06/writing-a-new-flarum-extension-on-ubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/06/writing-a-new-flarum-extension-on-ubuntu/">&lt;p&gt;In a previous post I described &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;08&#x2F;22&#x2F;installing-flarum-on-ubuntu-18-04&#x2F;&quot;&gt;how to install Flarum locally on Ubuntu&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is how I set up my development environment on top of that setup so I was able to write a new Flarum extension and test it on my local machine.&lt;&#x2F;p&gt;
&lt;p&gt;Recap: I installed Apache and PHP and enabled mod_rewrite, installed MariaDB and made a database, installed Composer, and used Composer to install Flarum at &#x2F;var&#x2F;www&#x2F;html&#x2F;flarum.&lt;&#x2F;p&gt;
&lt;p&gt;More info: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;flarum&#x2F;flarum&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md&quot;&gt;Flarum Contribution Guide&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discuss.flarum.org&#x2F;d&#x2F;1608-extension-development-using-composer-repositories-path&quot;&gt;extension development guide&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flarum.org&#x2F;docs&#x2F;extend&#x2F;start&#x2F;&quot;&gt;extension development quick start&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discuss.flarum.org&#x2F;d&#x2F;6320-extension-developers-show-us-your-workflow&#x2F;3&quot;&gt;workflow discussion&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to call my extension &quot;rabbitescape&#x2F;flarum-ext-rabbitescape-leveleditor&quot;, so that is the name you will see below.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I did:&lt;&#x2F;p&gt;
&lt;pre&gt;cd &#x2F;var&#x2F;www&#x2F;html&#x2F;flarum
&lt;&#x2F;pre&gt;
&lt;p&gt;Edit &#x2F;var&#x2F;www&#x2F;html&#x2F;flarum&#x2F;composer.json (see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discuss.flarum.org&#x2F;d&#x2F;1608-extension-development-using-composer-repositories-path&quot;&gt;the workbench explanation&lt;&#x2F;a&gt; for examples of the full file).&lt;&#x2F;p&gt;
&lt;p&gt;At the end of the &quot;require&quot; section I added a line like this:&lt;&#x2F;p&gt;
&lt;pre&gt;        &quot;rabbitescape&#x2F;flarum-ext-rabbitescape-leveleditor&quot;: &quot;*@dev&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;(Remembering to add a comma at the end of the previous line so it remained valid JSON.)&lt;&#x2F;p&gt;
&lt;p&gt;After the &quot;require&quot; section, I added this:&lt;&#x2F;p&gt;
&lt;pre&gt;    &quot;repositories&quot;: [
        {
            &quot;type&quot;: &quot;path&quot;,
            &quot;url&quot;: &quot;workbench&#x2F;*&#x2F;&quot;
        }
    ],
&lt;&#x2F;pre&gt;
&lt;p&gt;Next, I made the place where my extension would live:&lt;&#x2F;p&gt;
&lt;pre&gt;mkdir workbench
cd workbench
mkdir flarum-ext-rabbitescape-leveleditor
cd flarum-ext-rabbitescape-leveleditor
&lt;&#x2F;pre&gt;
&lt;p&gt;Now I created a file inside flarum-ext-rabbitescape-leveleditor called composer.json like this:&lt;&#x2F;p&gt;
&lt;pre&gt;{
    &quot;name&quot;: &quot;rabbitescape&#x2F;flarum-ext-rabbitescape-leveleditor&quot;,
    &quot;description&quot;: &quot;Allow viewing and editing Rabbit Escape levels in posts&quot;,
    &quot;type&quot;: &quot;flarum-extension&quot;,
    &quot;keywords&quot;: [&quot;rabbitescape&quot;],
    &quot;license&quot;: &quot;MIT&quot;,
    &quot;authors&quot;: [
        {
            &quot;name&quot;: &quot;Andy Balaam&quot;,
            &quot;email&quot;: &quot;andybalaam@artificialworlds.net&quot;
        }
    ],
    &quot;support&quot;: {
        &quot;issues&quot;: &quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;flarum-ext-rabbitescape-leveleditor&#x2F;issues&quot;,
        &quot;source&quot;: &quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;flarum-ext-rabbitescape-leveleditor&quot;
    },
    &quot;require&quot;: {
        &quot;flarum&#x2F;core&quot;: &quot;^0.1.0-beta.5&quot;
    },
    &quot;extra&quot;: {
        &quot;flarum-extension&quot;: {
            &quot;title&quot;: &quot;Rabbit Escape Level Editor&quot;
        }
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;In the same directory I created a file bootstrap.php like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;?php

return function () {
    echo &#x27;Hello, world!&#x27;;
};
&lt;&#x2F;pre&gt;
&lt;p&gt;Then I told Composer to refresh the project like this:&lt;&#x2F;p&gt;
&lt;pre&gt;cd ..&#x2F;..   # Back up to the main directory
composer update
&lt;&#x2F;pre&gt;
&lt;p&gt;Among the output I saw a line like this which convinced me it had worked:&lt;&#x2F;p&gt;
&lt;pre&gt;  - Installing rabbitescape&#x2F;flarum-ext-rabbitescape-leveleditor (dev-master): Symlinking from workbench&#x2F;flarum-ext-rabbitescape-leveleditor
&lt;&#x2F;pre&gt;
&lt;p&gt;I edited config.php in the main directory, and set &#x27;debug&#x27; =&amp;gt; true instead of false.&lt;&#x2F;p&gt;
&lt;p&gt;Now I went to http:&#x2F;&#x2F;localhost&#x2F;flarum, signed in as admin, adminpassword (as I set up in the previous post), clicked my username in the top right and chose Administration, then clicked Extensions on the left.&lt;&#x2F;p&gt;
&lt;p&gt;I saw my extension in the list, and turned it on by clicking the check box below it. This made the whole web site disappear, and instead just the text &quot;Hello world!&quot; appeared. This showed my extension was being loaded.&lt;&#x2F;p&gt;
&lt;p&gt;I edited bootstrap.php and commented out the line starting with &quot;echo&quot;. I refreshed the page in my browser and saw the Flarum site reappear.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, to install what&#x27;s needed for changing Flarum&#x27;s UI, I needed to run:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt install npm
sudo npm install --global gulp
&lt;&#x2F;pre&gt;
&lt;p&gt;Now, my extension is installed and ready to be developed! See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flarum.org&#x2F;docs&#x2F;extend&#x2F;start&#x2F;&quot;&gt;flarum.org&#x2F;docs&#x2F;extend&#x2F;start&lt;&#x2F;a&gt; for how to get started making it do cool stuff.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Ubuntu &quot;compose&quot; key for easy unicode character input</title>
          <pubDate>Wed, 05 Sep 2018 12:29:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/05/ubuntu-compose-key-for-easy-unicode-character-input/</link>
          <guid>https://artificialworlds.net/blog/2018/09/05/ubuntu-compose-key-for-easy-unicode-character-input/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/05/ubuntu-compose-key-for-easy-unicode-character-input/">&lt;p&gt;I found out on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;joinmastodon.org&#x2F;&quot;&gt;Mastodon&lt;&#x2F;a&gt; recently that the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Compose_key&quot;&gt;Compose key&lt;&#x2F;a&gt; exists, allowing you to enter special characters using easy-to-remember key sequences (e.g. &quot;&lt;em&gt;&amp;lt;compose&amp;gt;&lt;&#x2F;em&gt;&#x2F;=&quot; gives you &quot;â‰ &quot;).&lt;&#x2F;p&gt;
&lt;p&gt;To do this on Ubuntu (and probably many other systems), hold SHIFT, then press AltGr, release them both, and type something like &quot;&#x2F;=&quot;. You should see the â‰ symbol appear.&lt;&#x2F;p&gt;
&lt;p&gt;In Ubuntu MATE, you can customise what key is the compose key by opening &quot;Keyboard&quot;, clicking &quot;Layouts&quot;, then &quot;Options...&quot; and checking a box under &quot;Position of compose key&quot;. I checked &quot;Right Alt&quot;, meaning my &quot;AltGr&quot; key is now the compose key.&lt;&#x2F;p&gt;
&lt;p&gt;More info: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;help.ubuntu.com&#x2F;community&#x2F;ComposeKey&quot;&gt;Ubuntu ComposeKey docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Want to explore? See the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;help.ubuntu.com&#x2F;community&#x2F;GtkComposeTable&quot;&gt;list of the default sequences you can type&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Albert
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Sounds cool, but doesn’t seem to work in Ubuntu 16.04. Maybe it only works with a specific locale or xkeyboard layout?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Albert, I’m surprised to hear it doesn’t work for you, what a pity.  Are you sure you pressed SHIFT and ALT GR together, then released them and then pressed &quot;&#x2F;=&quot;?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Albert
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Found the problem. In System Settings &#x2F; Keyboard &#x2F; Shortcuts &#x2F; Typing, I had &quot;Compose Key&quot; set to Disabled. The help text underneath says &quot;To edit a shortcut, click the row and hold down the new keys …&quot;. That doesn’t work, even though it does for other shortcuts. However, clicking on Disabled gives a menu with no Shift-AltGr, but with &quot;Right Alt&quot; among others. After selecting that, it works (both with just AltGr, or Shift-AltGr). Thanks for this cool tip.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Brilliant, thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Albert
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Bad news: the Compose Key was disabled on my system for a good reason. If I set it to AltGr, I can no longer use it to type the various characters and accents which require it.&lt;br&#x2F;&gt;
So I guess this Compose Key trick only applies to a plain US keyboard layout. Sounds very useful for Americans, but only for Americans.&lt;br&#x2F;&gt;
Thanks anyway. It was intersting, and may be useful if I’m stuck some day in some Internet cafÃ© with a US layout.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Sorry to hear that.  It works on a UK keyboard too.  You could try re-mapping it to a different key entirely?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Redirecting all requests to https and www using .htaccess in Apache</title>
          <pubDate>Tue, 04 Sep 2018 07:23:57 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/09/04/redirecting-all-requests-to-https-and-www-using-htaccess-in-apache/</link>
          <guid>https://artificialworlds.net/blog/2018/09/04/redirecting-all-requests-to-https-and-www-using-htaccess-in-apache/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/09/04/redirecting-all-requests-to-https-and-www-using-htaccess-in-apache/">&lt;p&gt;I want all requests to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;rabbit-escape&#x2F;levels&#x2F;&quot;&gt;artificialworlds.net&#x2F;rabbit-escape&#x2F;levels&#x2F;&lt;&#x2F;a&gt; to get redirected to use the https protocol, and to include &quot;www.&quot; at the beginning of the URL, and I found lots of Stack Overflow articles, but nothing there worked perfectly for me. Here is how I managed it.&lt;&#x2F;p&gt;
&lt;p&gt;I edited the .htaccess file in the directory where I want this to apply, and added this at the top:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;IfModule mod_rewrite.c&amp;gt;
  RewriteEngine On
  RewriteCond %{HTTP_HOST} ^artificialworlds\.net$ [NC]
  RewriteRule ^ https:&#x2F;&#x2F;www.artificialworlds.net%{REQUEST_URI} [R=301,L]

  RewriteCond %{HTTPS} off
  RewriteRule ^ https:&#x2F;&#x2F;www.artificialworlds.net%{REQUEST_URI} [R=301,L]
&amp;lt;&#x2F;IfModule&amp;gt;
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Example of a systemd service file</title>
          <pubDate>Thu, 23 Aug 2018 13:21:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/08/23/example-of-a-systemd-service-file/</link>
          <guid>https://artificialworlds.net/blog/2018/08/23/example-of-a-systemd-service-file/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/08/23/example-of-a-systemd-service-file/">&lt;p&gt;Here is an almost-minimal example of a systemd service file, that I use to run the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&quot;&gt;Mastodon&lt;&#x2F;a&gt; bot of my generative art playground &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft&quot;&gt;Graft&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I made a dedicated user just to run this service, and installed Graft into &#x2F;home&#x2F;graft&#x2F;apps&#x2F;graft under that username. Now, as root, I edited a file called &#x2F;etc&#x2F;systemd&#x2F;system&#x2F;graft.service and made it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;[Service]
ExecStart=&#x2F;home&#x2F;graft&#x2F;apps&#x2F;graft&#x2F;bot-mastodon
User=graft
Group=graft
WorkingDirectory=&#x2F;home&#x2F;graft&#x2F;apps&#x2F;graft&#x2F;
[Install]
WantedBy=multi-user.target
&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can start the graft service like any other service like this:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo systemctl start graft&lt;&#x2F;pre&gt;
&lt;p&gt;and find out its status with:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo systemctl status graft&lt;&#x2F;pre&gt;
&lt;p&gt;If I want it to run on startup I can do:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo systemctl enable graft&lt;&#x2F;pre&gt;
&lt;p&gt;and it will. Easy!&lt;&#x2F;p&gt;
&lt;p&gt;If I want to look at its output, it&#x27;s:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo journalctl -u graft&lt;&#x2F;pre&gt;
&lt;p&gt;As a reward for reading this far, here&#x27;s a little animation you can make with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;graft&quot;&gt;Graft&lt;&#x2F;a&gt;:&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;tiddlers.gif&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want the service to be restarted whenever it fails, under [Service] add something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;Restart=always
RestartSec=3&lt;&#x2F;pre&gt;
&lt;p&gt;More info at: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jonarcher.info&#x2F;2015&#x2F;08&#x2F;ensure-systemd-services-restart-on-failure&#x2F;&quot;&gt;Jon Archer&#x27;s blog post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Installing Flarum on Ubuntu 18.04</title>
          <pubDate>Wed, 22 Aug 2018 19:44:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/08/22/installing-flarum-on-ubuntu-18-04/</link>
          <guid>https://artificialworlds.net/blog/2018/08/22/installing-flarum-on-ubuntu-18-04/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/08/22/installing-flarum-on-ubuntu-18-04/">&lt;p&gt;I am setting up a forum for sharing levels for my game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, and I have decided to try and use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;flarum.org&#x2F;&quot;&gt;Flarum&lt;&#x2F;a&gt;, because it looks really usable and responsive, has features we need like liking posts and following authors, and I think it will be reasonably OK to write the custom features we want.&lt;&#x2F;p&gt;
&lt;p&gt;So, I want a dev environment on my local Ubuntu 18.04 machine, and the first step to that is a standard install.&lt;&#x2F;p&gt;
&lt;p&gt;Warning: at the time of writing the Flarum docs say it does not work with PHP 7.2, which is what is included with Ubuntu 18.04, so this may not work. (So far it looks OK for me.)&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I got it working:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt install \
    apache2 \
    libapache2-mod-php \
    mariadb-server \
    php-mysql \
    php-json \
    php-gd \
    php-tokenizer \
    php-mbstring \
    php-curl

php -r &quot;copy(&#x27;https:&#x2F;&#x2F;getcomposer.org&#x2F;installer&#x27;, &#x27;composer-setup.php&#x27;);&quot;

# Get the next line from https:&#x2F;&#x2F;getcomposer.org&#x2F;download&#x2F;
# Don&#x27;t copy it exactly!
php -r &quot;if (hash_file(&#x27;SHA384&#x27;, &#x27;composer-setup.php&#x27;) === &#x27;544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061&#x27;) { echo &#x27;Installer verified&#x27;; } else { echo &#x27;Installer corrupt&#x27;; unlink(&#x27;composer-setup.php&#x27;); } echo PHP_EOL;&quot;

mkdir $HOME&#x2F;bin
php composer-setup.php --install-dir=$HOME&#x2F;bin&#x2F; --filename=composer
rm composer-setup.php

cd &#x2F;var&#x2F;www&#x2F;html
sudo mkdir flarum
sudo chown $(whoami) flarum

# Log out and in again here to get composer to be in your PATH
cd flarum
composer create-project flarum&#x2F;flarum . --stability=beta

sudo chgrp -R www-data .
sudo chmod -R 775 .

sudo a2enmod rewrite
&lt;&#x2F;pre&gt;
&lt;p&gt;Next I granted the permissions for Flarum&#x27;s .htaccess file to change settings by modifying &#x2F;etc&#x2F;apache2&#x2F;apache2.conf and adding this after the existing &amp;lt;Directory entries:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;Directory &quot;&#x2F;var&#x2F;www&#x2F;html&#x2F;flarum&#x2F;&quot;&amp;gt;
    AllowOverride All
&amp;lt;&#x2F;Directory&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;and restarted Apache:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo systemctl restart apache2
&lt;&#x2F;pre&gt;
&lt;p&gt;Now I created a database in MariaDB:&lt;&#x2F;p&gt;
&lt;pre&gt;$ sudo mariadb
MariaDB [(none)]&amp;gt; CREATE USER flarumuser IDENTIFIED BY &#x27;flarumpassword&#x27;;
MariaDB [(none)]&amp;gt; CREATE DATABASE flarumdb;
MariaDB [(none)]&amp;gt; GRANT ALL ON flarumdb.* TO flarumuser;
&lt;&#x2F;pre&gt;
&lt;p&gt;and went to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost&#x2F;flarum&quot;&gt;http:&#x2F;&#x2F;localhost&#x2F;flarum&lt;&#x2F;a&gt; in my browser, and entered these details:&lt;&#x2F;p&gt;
&lt;pre&gt;MySQL host: localhost
MySQL database: flarumdb
MySQL user: flarumuser
MySQL password: flarumpassword
table prefix: flarum_

Admin user: admin
Admin email: &amp;lt;my email address&amp;gt;
Admin password: adminpassword
&lt;&#x2F;pre&gt;
&lt;p&gt;Once I typed all those correctly and submitted, I had to wait a while, and then after it printed some details I refreshed the page and saw a local Flarum ready to customise.&lt;&#x2F;p&gt;
&lt;p&gt;Next, I looked into &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;09&#x2F;06&#x2F;writing-a-new-flarum-extension-on-ubuntu&#x2F;&quot;&gt;how to get an extension development environment up and running&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to find and share levels for Rabbit Escape, check up on our progress setting up the forum at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;levels&quot;&gt;https:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;levels&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape 0.11 out now!</title>
          <pubDate>Sun, 19 Aug 2018 16:57:34 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/08/19/rabbit-escape-0-11-out-now/</link>
          <guid>https://artificialworlds.net/blog/2018/08/19/rabbit-escape-0-11-out-now/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/08/19/rabbit-escape-0-11-out-now/">&lt;p&gt;The RABBOTS are coming!&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rabbot.gif&quot; alt=&quot;&quot; width=&quot;300&quot; height=&quot;255&quot; class=&quot;alignnone size-medium wp-image-2433&quot;&gt;
&lt;p&gt;Get the latest version of Rabbit Escape:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Android: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;f-droid.org&#x2F;en&#x2F;packages&#x2F;net.artificialworlds.rabbitescape&#x2F;&quot;&gt;Rabbit Escape on F-Droid&lt;&#x2F;a&gt; (free, no ads)&lt;&#x2F;li&gt;
&lt;li&gt;Android: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=net.artificialworlds.rabbitescapefree&quot;&gt;Rabbit Escape Free on the Play Store&lt;&#x2F;a&gt; (free, no ads)&lt;&#x2F;li&gt;
&lt;li&gt;Android: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=net.artificialworlds.rabbitescape&quot;&gt;Rabbit Escape on the Play Store&lt;&#x2F;a&gt; (Pay about 60p, identical to the free one!)&lt;&#x2F;li&gt;
&lt;li&gt;Linux, Windows, Mac and Android: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Direct from the Rabbit Escape web site&lt;&#x2F;a&gt; (free, no ads)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Migrating videos from YouTube to PeerTube inside a Docker container</title>
          <pubDate>Wed, 15 Aug 2018 09:21:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/08/15/migrating-videos-from-youtube-to-peertube-inside-a-docker-container/</link>
          <guid>https://artificialworlds.net/blog/2018/08/15/migrating-videos-from-youtube-to-peertube-inside-a-docker-container/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/08/15/migrating-videos-from-youtube-to-peertube-inside-a-docker-container/">&lt;p&gt;I have quite a few &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;ajbalaam&quot;&gt;videos hosted on YouTube&lt;&#x2F;a&gt; that I would like to upload to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;peertube.mastodon.host&#x2F;accounts&#x2F;andybalaam&#x2F;videos&quot;&gt;my new PeerTube location&lt;&#x2F;a&gt;, but I don&#x27;t want to install all the PeerTube dependencies on my machine, so I did it all inside a Docker image.&lt;&#x2F;p&gt;
&lt;p&gt;First I built and started a Docker container:&lt;&#x2F;p&gt;
&lt;pre&gt;$ git clone https:&#x2F;&#x2F;github.com&#x2F;chocobozzz&#x2F;PeerTube &#x2F;tmp&#x2F;peertube
$ cd &#x2F;tmp&#x2F;peertube
$ docker build . -f .&#x2F;support&#x2F;docker&#x2F;production&#x2F;Dockerfile.buster --tag peertube
$ docker run --tty --interactive peertube bash
&lt;&#x2F;pre&gt;
&lt;p&gt;Then I ran these commands inside it:&lt;&#x2F;p&gt;
&lt;pre&gt;# apt update &amp;amp;&amp;amp; apt install git -y &amp;amp;&amp;amp; apt upgrade -y
# npm install -g npm n typescript
# n stable
# yarn upgrade yarn
# NODE_ENV=develop NOCLIENT=1 yarn install --pure-lockfile --production=false
# NODE_ENV=develop npm run setup:cli
# node dist&#x2F;server&#x2F;tools&#x2F;peertube-import-videos.js --url &quot;https:&#x2F;&#x2F;peertube.mastodon.host&quot; --username &quot;andybalaam&quot; --target-url &quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=TG0qRDrUPpA&quot; --password=mypw
&lt;&#x2F;pre&gt;
&lt;p&gt;Of course, it would be better to write this up into its own Dockerfile to make this a one-liner.&lt;&#x2F;p&gt;
&lt;p&gt;References: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Chocobozzz&#x2F;PeerTube&#x2F;blob&#x2F;develop&#x2F;support&#x2F;doc&#x2F;docker.md&quot;&gt;PeerTube Docker setup&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Chocobozzz&#x2F;PeerTube&#x2F;blob&#x2F;develop&#x2F;support&#x2F;doc&#x2F;tools.md#import-videosjs&quot;&gt;PeerTube video import&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to write a programming language articles</title>
          <pubDate>Mon, 13 Aug 2018 11:10:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/08/13/how-to-write-a-programming-language-articles/</link>
          <guid>https://artificialworlds.net/blog/2018/08/13/how-to-write-a-programming-language-articles/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/08/13/how-to-write-a-programming-language-articles/">&lt;p&gt;Recent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journal&#x2F;overload_by_cover&quot;&gt;Overload&lt;&#x2F;a&gt; journal issues contain my new articles on How to Write a Programming Language.&lt;&#x2F;p&gt;
&lt;p&gt;Part 1: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;2510&quot;&gt;How to Write a Programming Language: Part 1, The Lexer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Part 2: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;2532&quot;&gt;How to Write a Programming Language: Part 2, The Parser&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;PDF of the latest issue: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;var&#x2F;uploads&#x2F;journals&#x2F;Overload146.pdf&quot;&gt;Overload 146&lt;&#x2F;a&gt; containing part 2.&lt;&#x2F;p&gt;
&lt;p&gt;This is all creative-commons licensed and developed in public at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;articles-how-to-write-a-programming-language&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;articles-how-to-write-a-programming-language&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Allow drag-to-side, but not drag-to-top in Ubuntu MATE (Marco)</title>
          <pubDate>Fri, 13 Jul 2018 11:22:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/07/13/allow-drag-to-side-but-not-drag-to-top-in-ubuntu-mate-marco/</link>
          <guid>https://artificialworlds.net/blog/2018/07/13/allow-drag-to-side-but-not-drag-to-top-in-ubuntu-mate-marco/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/07/13/allow-drag-to-side-but-not-drag-to-top-in-ubuntu-mate-marco/">&lt;p&gt;I love the &quot;tiling&quot; feature in many window managers including &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mate-desktop&#x2F;marco&quot;&gt;Marco&lt;&#x2F;a&gt; that means I can drag windows to the side of the screen and get them covering one half.&lt;&#x2F;p&gt;
&lt;p&gt;However, I never use the similar feature that allows dragging a window to the top, and it often triggers when I just want to move a window upwards.&lt;&#x2F;p&gt;
&lt;p&gt;Today I discovered that Marco (the non-fancy window manager in Ubuntu MATE and probably other places) does allow me to have one without the other, even though the UI configuration tools don&#x27;t expose the option.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I did:&lt;&#x2F;p&gt;
&lt;pre&gt;gsettings set org.mate.Marco.general allow-top-tiling false
&lt;&#x2F;pre&gt;
&lt;p&gt;Now my windows can be dragged to the side for half-screen maximisation, but not to the top for full-screen!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Connecting to Slack from an IRC client using slirc</title>
          <pubDate>Fri, 15 Jun 2018 11:03:54 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/06/15/connecting-to-slack-from-an-irc-client-using-slirc/</link>
          <guid>https://artificialworlds.net/blog/2018/06/15/connecting-to-slack-from-an-irc-client-using-slirc/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/06/15/connecting-to-slack-from-an-irc-client-using-slirc/">&lt;p&gt;I tried to get back to an IRC interface to Slack &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;06&#x2F;08&#x2F;using-matrix-to-connect-to-slack-from-an-irc-client-on-ubuntu&#x2F;&quot;&gt;using Matrix&lt;&#x2F;a&gt;, and it had some problems. Thanks to Colin Watson&#x27;s comment on that post, I tried Daniel Beer&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.dlbeer.co.nz&#x2F;articles&#x2F;slirc&#x2F;index.html&quot;&gt;slirc&lt;&#x2F;a&gt;, and so far it seems to be working pretty well.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I did:&lt;&#x2F;p&gt;
&lt;p&gt;Get a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.slack.com&#x2F;custom-integrations&#x2F;legacy-tokens&quot;&gt;Slack legacy token&lt;&#x2F;a&gt; which slirc will use to connect to Slack as you. Follow the instructions given at that link, and you should end up with a token that looks something like &quot;abcd-123456768-ETC-ETC&quot;. Keep a note of it.&lt;&#x2F;p&gt;
&lt;p&gt;Install the prerequisites for slirc, and download it:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo cpan AnyEvent AnyEvent::HTTP AnyEvent::Socket AnyEvent::WebSocket::Client URI::Encode Data::Dumper JSON
mkdir slirc
cd slirc
wget -q &#x27;https:&#x2F;&#x2F;www.dlbeer.co.nz&#x2F;articles&#x2F;slirc&#x2F;slirc-20180515.pl&#x27;
chmod +x slirc-20180515.pl&lt;&#x2F;pre&gt;
&lt;p&gt;Create a file in the slirc directory you created above, called rc.conf, and make it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;slack_token=abcd-123456768-ETC-ETC
password=somepassword
port=6667&lt;&#x2F;pre&gt;
&lt;p&gt;Replace &quot;abcd-123456768-ETC-ETC&quot; with the Slack legacy token you noted down earlier.&lt;&#x2F;p&gt;
&lt;p&gt;Replace &quot;somepassword&quot; with something you&#x27;ve made up (not your Slack password) - this is what you will type as the password in your IRC client.&lt;&#x2F;p&gt;
&lt;p&gt;Run slirc and leave it running:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;slirc-20180515.pl rc.conf&lt;&#x2F;pre&gt;
&lt;p&gt;(Make sure you are inside the slirc dir when you run that.)&lt;&#x2F;p&gt;
&lt;p&gt;Start your IRC client (e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hexchat.github.io&#x2F;&quot;&gt;HexChat&lt;&#x2F;a&gt;) and add a server with address &quot;localhost&quot; and port 6667, with your slack username and the password you added in the rc.conf (which you wrote instead of &quot;somepassword&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;This mostly works for me, except it has a tendency to open a load of ad-hoc chats as channels, so I have to close them all to get a usable list.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using Matrix to connect to Slack from an IRC client on Ubuntu</title>
          <pubDate>Fri, 08 Jun 2018 16:08:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/06/08/using-matrix-to-connect-to-slack-from-an-irc-client-on-ubuntu/</link>
          <guid>https://artificialworlds.net/blog/2018/06/08/using-matrix-to-connect-to-slack-from-an-irc-client-on-ubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/06/08/using-matrix-to-connect-to-slack-from-an-irc-client-on-ubuntu/">&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; I found a better solution &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;06&#x2F;15&#x2F;connecting-to-slack-from-an-irc-client-using-slirc&#x2F;&quot;&gt;using slirc&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I like using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hexchat.github.io&#x2F;&quot;&gt;HexChat&lt;&#x2F;a&gt; to talk to my colleagues, like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;1782&#x2F;&quot;&gt;one other guy&lt;&#x2F;a&gt;. It is fast, and it pops up a new window when someone sends me a direct&#x2F;private message.&lt;&#x2F;p&gt;
&lt;p&gt;Recently, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;drewdevault.com&#x2F;2015&#x2F;11&#x2F;01&#x2F;Please-stop-using-slack.html&quot;&gt;Slack&lt;&#x2F;a&gt; shut down their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theregister.co.uk&#x2F;2018&#x2F;03&#x2F;09&#x2F;slack_cuts_ties_to_irc_and_xmpp&#x2F;&quot;&gt;IRC gateway&lt;&#x2F;a&gt;, forcing me to use their slow UI, and making me unable to be responsive to direct messages without spending my entire life checking said UI, or being disturbed by notifications.&lt;&#x2F;p&gt;
&lt;p&gt;So, I decided to set up a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;matrix.org&quot;&gt;Matrix&lt;&#x2F;a&gt; bridge, because how hard can it be?&lt;&#x2F;p&gt;
&lt;p&gt;My system is Ubuntu MATE 18.04.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-i-did&quot;&gt;What I did&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;install-synapse&quot;&gt;Install Synapse&lt;&#x2F;h3&gt;
&lt;p&gt;Install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;synapse&quot;&gt;Synapse&lt;&#x2F;a&gt;, a Matrix homeserver.&lt;&#x2F;p&gt;
&lt;pre&gt;wget -qO - https:&#x2F;&#x2F;matrix.org&#x2F;packages&#x2F;debian&#x2F;repo-key.asc | sudo apt-key add -
sudo apt-add-repository http:&#x2F;&#x2F;matrix.org&#x2F;packages&#x2F;debian&#x2F;
sudo apt update
sudo apt install matrix-synapse&lt;&#x2F;pre&gt;
&lt;p&gt;Edit &#x2F;etc&#x2F;matrix-synapse&#x2F;homeserver.yaml to change the line containing &quot;registration_shared_secret&quot; to look something like:&lt;&#x2F;p&gt;
&lt;pre&gt;registration_shared_secret: &quot;some secret password you made up&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;(Replacing &quot;some secret password you made up&quot; with something secret.)&lt;&#x2F;p&gt;
&lt;p&gt;Make a new user:&lt;&#x2F;p&gt;
&lt;pre&gt;register_new_matrix_user -c &#x2F;etc&#x2F;matrix-synapse&#x2F;homeserver.yaml http:&#x2F;&#x2F;localhost:8008&lt;&#x2F;pre&gt;
&lt;p&gt;Enter a username and password, and take a note of them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;install-riot&quot;&gt;Install Riot&lt;&#x2F;h3&gt;
&lt;p&gt;(Technically, this step is optional, but I definitely recommend it to check everything is working.)&lt;&#x2F;p&gt;
&lt;p&gt;Install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;riot.im&#x2F;&quot;&gt;Riot&lt;&#x2F;a&gt;, a client that can talk to a homeserver.&lt;&#x2F;p&gt;
&lt;pre&gt;curl https:&#x2F;&#x2F;riot.im&#x2F;packages&#x2F;debian&#x2F;repo-key.asc | sudo apt-key add -
sudo apt-add-repository https:&#x2F;&#x2F;riot.im&#x2F;packages&#x2F;debian&#x2F;
sudo apt update
sudo apt install riot-web&lt;&#x2F;pre&gt;
&lt;p&gt;Start Riot from the menu or by typing riot-web in a console.&lt;&#x2F;p&gt;
&lt;p&gt;Choose to log in to a Custom Server (not create a new user), and enter the Home Server URL as http:&#x2F;&#x2F;localhost:8008, leave the Identity Server URL as it is, and enter the username and password you entered in the previous step.&lt;&#x2F;p&gt;
&lt;p&gt;Riot should be able to connect to the Synapse server, even though it won&#x27;t have any rooms in it yet.&lt;&#x2F;p&gt;
&lt;p&gt;If this doesn&#x27;t work, try running riot-web in a console, and check the Synapse log file at &#x2F;var&#x2F;log&#x2F;matrix-synapse&#x2F;homeserver.log&lt;&#x2F;p&gt;
&lt;h3 id=&quot;install-matrix-puppet-slack&quot;&gt;Install matrix-puppet-slack&lt;&#x2F;h3&gt;
&lt;p&gt;(Note: after this step, the homeserver can only be for you now, because it is logged into Slack as you. If you want to make a new Matrix server for lots of people, I found that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-appservice-slack&quot;&gt;matrix-appservice-slack&lt;&#x2F;a&gt; works, but this only connects channels, not direct messages.)&lt;&#x2F;p&gt;
&lt;p&gt;Get a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.slack.com&#x2F;custom-integrations&#x2F;legacy-tokens&quot;&gt;Slack legacy token&lt;&#x2F;a&gt; which matrix-puppet-slack will use to connect to Slack as you. Follow the instructions given at that link, and you should end up with a token that looks something like &quot;abcd-123456768-ETC-ETC&quot;. Keep a note of it.&lt;&#x2F;p&gt;
&lt;p&gt;Install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-hacks&#x2F;matrix-puppet-slack&quot;&gt;matrix-puppet-slack&lt;&#x2F;a&gt; which allows you to connect a homeserver to a Slack instance, pretending it is you:&lt;&#x2F;p&gt;
&lt;pre&gt;cd
git clone https:&#x2F;&#x2F;github.com&#x2F;matrix-hacks&#x2F;matrix-puppet-slack.git
cd matrix-puppet-slack&#x2F;
npm install
cp config.sample.json config.json&lt;&#x2F;pre&gt;
&lt;p&gt;Edit config.json so it looks something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;{
  &quot;slack&quot;: [{
    &quot;team_name&quot;: &quot;Name of my team&quot;,
    &quot;user_access_token&quot;: &quot;abcd-123456768-ETC-ETC&quot;
  }],
  &quot;registrationPath&quot;: &quot;slack-registration.yaml&quot;,
  &quot;port&quot;: 8090,
  &quot;bridge&quot;: {
    &quot;homeserverUrl&quot;:&quot;http:&#x2F;&#x2F;localhost:8008&quot;,
    &quot;domain&quot;: &quot;localhost&quot;,
    &quot;registration&quot;: &quot;slack-registration.yaml&quot;
  }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Now generate a config file that Synapse will use, called slack-registration.yaml, by typing this command (still in the matrix-puppet-slack directory):&lt;&#x2F;p&gt;
&lt;pre&gt;node index.js -r -u &quot;http:&#x2F;&#x2F;localhost:8090&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;When asked, type in the username and password you entered in the first step (when you ran the register_new_matrix_user command).&lt;&#x2F;p&gt;
&lt;p&gt;This creates a config file telling Synapse that matrix-puppet-slack is running on the local machine on port 8090.&lt;&#x2F;p&gt;
&lt;p&gt;Now, run it:&lt;&#x2F;p&gt;
&lt;pre&gt;node index.js&lt;&#x2F;pre&gt;
&lt;p&gt;and edit Synapse&#x27;s config file &#x2F;etc&#x2F;matrix-synapse&#x2F;homeserver.yaml to point at the new config file you just made, by editing the line mentioning app_service_config_files so that it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;app_service_config_files: [
    &quot;&#x2F;path&#x2F;to&#x2F;matrix-puppet-slack&#x2F;slack-registration.yaml&quot;
]&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure you replace &quot;&#x2F;path&#x2F;to&#x2F;matrix-puppet-slack&quot; in the above with the directory where you put matrix-puppet-slack, which is &#x2F;home&#x2F;yourusername&#x2F;matrix-puppet-slack if you followed the instructions exactly.&lt;&#x2F;p&gt;
&lt;p&gt;Restart Synapse, and when you go back into Riot, you should see your Slack channels appear gradually (as people talk in them):&lt;&#x2F;p&gt;
&lt;pre&gt;sudo service matrix-synapse restart&lt;&#x2F;pre&gt;
&lt;p&gt;If not, check the output from the node index.js command, and the Synapse log file at &#x2F;var&#x2F;log&#x2F;matrix-synapse&#x2F;homeserver.log.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;install-matrix-ircd&quot;&gt;Install matrix-ircd&lt;&#x2F;h3&gt;
&lt;p&gt;To allow connecting to Matrix from an IRC client, install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-ircd&quot;&gt;matrix-ircd&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;cd
git clone https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-ircd.git
cd matrix-ircd
cargo build
cargo run -- --url &quot;http:&#x2F;&#x2F;localhost:8008&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;(Note this works on Ubuntu 18.04, but operating systems with older versions of Rust may need to get the latest first - see the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-org&#x2F;matrix-ircd&quot;&gt;matrix-ircd&lt;&#x2F;a&gt; page for details.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;connect-from-an-irc-client&quot;&gt;Connect from an IRC client&lt;&#x2F;h3&gt;
&lt;p&gt;Now use an IRC client to connect to matrix-ird. I recommend &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hexchat.github.io&#x2F;&quot;&gt;HexChat&lt;&#x2F;a&gt; if you&#x27;re looking for one.&lt;&#x2F;p&gt;
&lt;p&gt;matrix-ircd runs on port 5999 by default, so you should be able to connect to it from your IRC client by setting the server to localhost&#x2F;5999 and using the username and password from the first step (when you ran the register_new_matrix_user command). Use plain username&#x2F;password authentication.&lt;&#x2F;p&gt;
&lt;p&gt;If it works, you should see your Slack channels appear in your IRC client. If not check the logs mentioned before, and the error messages and logs from your IRC client.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-works-and-doesn-t-work&quot;&gt;What works and doesn&#x27;t work&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Messages typed by me and others into IRC, Riot and Slack appear in all the other places, including direct messages.&lt;&#x2F;li&gt;
&lt;li&gt;Synapse and Riot are installed fully, so Synapse will be running after a reboot, and Riot is available in the menus, but matrix-puppet-slack and matrix-ircd are just running in a console, and have to be manually started. It should be reasonably simple to make custom systemd files to start them automatically.&lt;&#x2F;li&gt;
&lt;li&gt;matrix-puppet-slack crashed once and I had to restart it, so this may not be very reliable. I logged &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;matrix-hacks&#x2F;matrix-puppet-slack&#x2F;issues&#x2F;40&quot;&gt;an issue&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;The Slack channel names look terrible in my IRC client. I think matrix-ircd needs to find the pretty names (which are displayed correctly in Riot) and use them instead of the coded names used under the cover in Matrix.&lt;&#x2F;li&gt;
&lt;li&gt;My first ever direct message to someone does not work, even though I can choose them and attempt to send a message. As soon as they say something to me, they appear as a channel in Riot and IRC, and I can talk back and forth no problem from then on.&lt;&#x2F;li&gt;
&lt;li&gt;Direct messages look like normal channels in Matrix, which means I can&#x27;t use HexChat to pop up notifications for them, so this was all pointless.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Please leave comments below if you know how I can fix any of these problems.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusions&quot;&gt;Conclusions&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Matrix seems really cool&lt;&#x2F;li&gt;
&lt;li&gt;It is basically possible to bridge IRC-Matrix-Slack as I wanted&lt;&#x2F;li&gt;
&lt;li&gt;But, some remaining bugs mean I have to keep using Slack&#x27;s UI for now :-(&lt;&#x2F;li&gt;
&lt;li&gt;Please comment telling me how to do this better&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Colin Watson
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’ve been using slirc (&lt;a href=&quot;https:&#x2F;&#x2F;www.dlbeer.co.nz&#x2F;articles&#x2F;slirc&#x2F;index.html&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.dlbeer.co.nz&#x2F;articles&#x2F;slirc&#x2F;index.html&lt;&#x2F;a&gt;) since the gateway shutdown.  It’s pretty simple and doesn’t have too many moving parts to go wrong.  Not everything’s perfect, but it’s been working tolerably well for me.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Colin, thanks!  More success that way: &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2018&#x2F;06&#x2F;15&#x2F;connecting-to-slack-from-an-irc-client-using-slirc&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2018&#x2F;06&#x2F;...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    kenf
    &lt;span class=&quot;comment_date&quot;&gt;2021-02-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;Can you please guide me into how to issue the link command after setting up the bridge. I don’t know where&#x2F;how to set the administration control room&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2021-02-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi kenf, I’m afraid I don’t understand the question, which means I am probably not the right person to ask, sorry.  I suggest asking the matrix-puppet-slack people.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Clever Things People Do In Groovy So You Have To Know About Them video</title>
          <pubDate>Sun, 03 Jun 2018 16:34:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/06/03/clever-things-people-do-in-groovy-so-you-have-to-know-about-them-video/</link>
          <guid>https://artificialworlds.net/blog/2018/06/03/clever-things-people-do-in-groovy-so-you-have-to-know-about-them-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/06/03/clever-things-people-do-in-groovy-so-you-have-to-know-about-them-video/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.groovy-lang.org&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt; has lots of interesting syntax that can be used for domain-specific languages, such as &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gradle.org&#x2F;&quot;&gt;Gradle&lt;&#x2F;a&gt; build files, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jenkins.io&#x2F;doc&#x2F;book&#x2F;pipeline&#x2F;jenkinsfile&#x2F;&quot;&gt;Jenkinsfiles&lt;&#x2F;a&gt;. I try to demystify the syntax tricks a bit so you have a chance to read and understand what the code is actually doing:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Clever Things People Do In Groovy So You Have To Know About Them&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1aaf6fc6-854f-457c-88fa-ae89d0c038bc&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;groovy-clever-things&#x2F;clever-things.html&quot;&gt;Slides&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;videos-groovy-clever-things&quot;&gt;source code&lt;&#x2F;a&gt; are available.&lt;&#x2F;p&gt;
&lt;p&gt;Related: &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;27&#x2F;gradle-what-is-a-task-and-how-can-i-make-a-task-depend-on-another-task&#x2F;&quot;&gt;Gradle: what is a task, and how can I make a task depend on another task?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Gradle: what is a task, and how can I make a task depend on another task? – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] [Related: Clever things people do in Groovy so you have to know about them] […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Examples of SQL join types (LEFT JOIN, INNER JOIN etc.)</title>
          <pubDate>Thu, 26 Apr 2018 11:22:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/04/26/examples-of-sql-join-types-left-join-inner-join-etc/</link>
          <guid>https://artificialworlds.net/blog/2018/04/26/examples-of-sql-join-types-left-join-inner-join-etc/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/04/26/examples-of-sql-join-types-left-join-inner-join-etc/">&lt;p&gt;I have 2 tables like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_a;
+------+------+
| id   | name |
+------+------+
|    1 | row1 |
|    2 | row2 |
+------+------+

&amp;gt; SELECT * FROM table_b;
+------+------+------+
| id   | name | aid  |
+------+------+------+
|    3 | row3 |    1 |
|    4 | row4 |    1 |
|    5 | row5 | NULL |
+------+------+------+
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;inner-join-cares-about-both-tables&quot;&gt;INNER JOIN cares about both tables&lt;&#x2F;h2&gt;
&lt;p&gt;INNER JOIN cares about both tables, so you only get a row if both tables have one. If there is more than one matching pair, you get multiple rows.&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_a a INNER JOIN table_b b ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | id   | name | aid  |
+------+------+------+------+------+
|    1 | row1 |    3 | row3 | 1    |
|    1 | row1 |    4 | row4 | 1    |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;It makes no difference to INNER JOIN if you reverse the order, because it cares about both tables:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_b b INNER JOIN table_a a ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | aid  | id   | name |
+------+------+------+------+------+
|    3 | row3 | 1    |    1 | row1 |
|    4 | row4 | 1    |    1 | row1 |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;You get the same rows, but the columns are in a different order because we mentioned the tables in a different order.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;left-join-only-cares-about-the-first-table&quot;&gt;LEFT JOIN only cares about the first table&lt;&#x2F;h2&gt;
&lt;p&gt;LEFT JOIN cares about the first table you give it, and doesn&#x27;t care much about the second, so you always get the rows from the first table, even if there is no corresponding row in the second:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_a a LEFT JOIN table_b b ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | id   | name | aid  |
+------+------+------+------+------+
|    1 | row1 |    3 | row3 | 1    |
|    1 | row1 |    4 | row4 | 1    |
|    2 | row2 | NULL | NULL | NULL |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;Above you can see all rows of table_a even though some of them do not match with anything in table b, but not all rows of table_b - only ones that match something in table_a.&lt;&#x2F;p&gt;
&lt;p&gt;If we reverse the order of the tables, LEFT JOIN behaves differently:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_b b LEFT JOIN table_a a ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | aid  | id   | name |
+------+------+------+------+------+
|    3 | row3 | 1    |    1 | row1 |
|    4 | row4 | 1    |    1 | row1 |
|    5 | row5 | NULL | NULL | NULL |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;Now we get all rows of table_b, but only matching rows of table_a.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;right-join-only-cares-about-the-second-table&quot;&gt;RIGHT JOIN only cares about the second table&lt;&#x2F;h2&gt;
&lt;p&gt;a RIGHT JOIN b gets you exactly the same rows as b LEFT JOIN a. The only difference is the default order of the columns.&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_a a RIGHT JOIN table_b b ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | id   | name | aid  |
+------+------+------+------+------+
|    1 | row1 |    3 | row3 | 1    |
|    1 | row1 |    4 | row4 | 1    |
| NULL | NULL |    5 | row5 | NULL |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;This is the same rows as table_b LEFT JOIN table_a, which we saw in the LEFT JOIN section.&lt;&#x2F;p&gt;
&lt;p&gt;Similarly:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_b b RIGHT JOIN table_a a ON a.id=b.aid;
+------+------+------+------+------+
| id   | name | aid  | id   | name |
+------+------+------+------+------+
|    3 | row3 | 1    |    1 | row1 |
|    4 | row4 | 1    |    1 | row1 |
| NULL | NULL | NULL |    2 | row2 |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;p&gt;Is the same rows as table_a LEFT JOIN table_b.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-join-at-all-gives-you-copies-of-everything&quot;&gt;No join at all gives you copies of everything&lt;&#x2F;h2&gt;
&lt;p&gt;If you write your tables with no JOIN clause at all, just separated by commas, you get every row of the first table written next to every row of the second table, in every possible combination:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;gt; SELECT * FROM table_b b, table_a;
+------+------+------+------+------+
| id   | name | aid  | id   | name |
+------+------+------+------+------+
|    3 | row3 | 1    |    1 | row1 |
|    3 | row3 | 1    |    2 | row2 |
|    4 | row4 | 1    |    1 | row1 |
|    4 | row4 | 1    |    2 | row2 |
|    5 | row5 | NULL |    1 | row1 |
|    5 | row5 | NULL |    2 | row2 |
+------+------+------+------+------+
&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    MySQL: Quick breakdown of the types of joins [duplicate]
    &lt;span class=&quot;comment_date&quot;&gt;2022-11-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;… […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Fixing Slack emojis in HexChat</title>
          <pubDate>Fri, 16 Feb 2018 15:53:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/02/16/fixing-slack-emojis-in-hexchat/</link>
          <guid>https://artificialworlds.net/blog/2018/02/16/fixing-slack-emojis-in-hexchat/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/02/16/fixing-slack-emojis-in-hexchat/">&lt;p&gt;If, like me, you are this person:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;imgs.xkcd.com&#x2F;comics&#x2F;team_chat.png&quot; alt=&quot;&quot; &#x2F;&gt;(Source: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;1782&#x2F;&quot;&gt;xkcd.com&#x2F;1782&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;You may want to fix the stupid :slightly_smiling_face: messages you receive from Slack via the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;get.slack.help&#x2F;hc&#x2F;en-us&#x2F;articles&#x2F;201727913-Connect-to-Slack-over-IRC-and-XMPP&quot;&gt;IRC gateway&lt;&#x2F;a&gt;. Obviously, I&#x27;d prefer they went away entirely, but it&#x27;s still better to see a character than being spammed with colon abominations all over the place.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll need the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;emoji&quot;&gt;Python emoji package&lt;&#x2F;a&gt;, and a HexChat plugin like this:&lt;&#x2F;p&gt;
&lt;pre&gt;# Replace all the horrible :slightly_smiling_face: rubbish that Slack inserts
# into horrible Unicode emoji symbols.
# Author: Andy Balaam
# License: CC0 https:&#x2F;&#x2F;creativecommons.org&#x2F;publicdomain&#x2F;zero&#x2F;1.0&#x2F;

# Requires https:&#x2F;&#x2F;pypi.python.org&#x2F;pypi&#x2F;emoji - I used 0.4.5
# I manually copied the emoji dir into:
# &#x2F;home&#x2F;andrebal&#x2F;.local&#x2F;lib&#x2F;python2.7&#x2F;site-packages
import emoji
import hexchat

__module_name__ = &quot;slack-emojis&quot;
__module_version__ = &quot;1.0&quot;
__module_description__ = &quot;Translate emojis from Slack with colons into emojis&quot;

print &quot;Loading slack-emojis&quot;
chmsg = &quot;Channel Message&quot;
prmsg = &quot;Private Message to Dialog&quot;

def preprint(words, word_eol, userdata):
    txt = word_eol[1]
    replaced = emoji.emojize(txt, use_aliases=True)
    if replaced != txt:
        hexchat.emit_print(
            userdata[&quot;msgtype&quot;],
            words[0],
            replaced.encode(&#x27;utf-8&#x27;),
        )
        return hexchat.EAT_HEXCHAT
    else:
        return hexchat.EAT_NONE

hexchat.hook_print(chmsg, preprint, {&quot;msgtype&quot;: chmsg})
hexchat.hook_print(prmsg, preprint, {&quot;msgtype&quot;: prmsg})&lt;&#x2F;pre&gt;
&lt;p&gt;According to the page linked above, Slack are retiring the IRC gateway, which will make me very unhappy.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; added support for private messages too.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Victor
    &lt;span class=&quot;comment_date&quot;&gt;2018-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey there,&lt;&#x2F;p&gt;
&lt;p&gt;I am not that coding expert, I just use wordpress and its plugins.&lt;&#x2F;p&gt;
&lt;p&gt;Sorry for the noob question… Is there a way to make it work as a Wordpress plugin?&lt;&#x2F;p&gt;
&lt;p&gt;(I have connected a slack channel to create posts in wordpress, through zapier. The posts are created with the abomination :smile: stuff… )&lt;&#x2F;p&gt;
&lt;p&gt;Thanks in advance for your reply&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-04-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Victor, my plugin is written in Python, and WordPress plugins have to be PHP as far as I know, so there is no direct way to use it, I’m afraid :–(&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Eitan Adler
    &lt;span class=&quot;comment_date&quot;&gt;2018-07-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I added this here for more visibility: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hexchat&#x2F;hexchat-addons&#x2F;tree&#x2F;master&#x2F;python&#x2F;emoji-slack-fix&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;hexchat&#x2F;hexchat-addons&#x2F;tre...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-07-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Awesome thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Ideas on how lexing will work in Pepper3</title>
          <pubDate>Fri, 09 Feb 2018 13:14:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/02/09/ideas-on-how-lexing-will-work-in-pepper3/</link>
          <guid>https://artificialworlds.net/blog/2018/02/09/ideas-on-how-lexing-will-work-in-pepper3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/02/09/ideas-on-how-lexing-will-work-in-pepper3/">&lt;p&gt;I am trying to practice documentation-driven development in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;pepper&quot;&gt;Pepper3&lt;&#x2F;a&gt;, so every time I start on an area, I will write documentation explaining how it works, and include examples that are automatically verified during the build.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve started work on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lexical_analysis&quot;&gt;lexing&lt;&#x2F;a&gt;, since you can&#x27;t do much before you do that, but in fact, of course, I need to have a command line interface before I can verify any of the examples, so I&#x27;m working on that too.&lt;&#x2F;p&gt;
&lt;p&gt;Lexing is the process that takes a stream of characters (e.g. from a file) and turns it into a stream of &quot;tokens&quot; that are chunks of code like a variable name, a number or a string. (There is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&#x2F;blob&#x2F;master&#x2F;lexing.md&quot;&gt;more on lexing&lt;&#x2F;a&gt; in my mini programming language, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&quot;&gt;Cell&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;My thoughts so far about lexing are in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;pepper&#x2F;blob&#x2F;master&#x2F;doc&#x2F;lexing.md&quot;&gt;lexing.md&lt;&#x2F;a&gt;, and current ideas about command line interface are at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;pepper&#x2F;blob&#x2F;master&#x2F;doc&#x2F;command_line.md&quot;&gt;command_line.md&lt;&#x2F;a&gt;. All very much subject to change.&lt;&#x2F;p&gt;
&lt;p&gt;Headlines:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Ordinary programmers can write their own lexing rules.&lt;&#x2F;li&gt;
&lt;li&gt;Operators (functions like &quot;+&quot; that find their arguments on their left and right, instead of between brackets like normal functions) are defined at the lexing phase, so any symbol (e.g. &quot;in&quot;) can be an operator if you want.&lt;&#x2F;li&gt;
&lt;li&gt;Anything you might want to do with a pepper program, including running it, compiling it, packaging it for an distribution system, should be available as a sub-command of the main pepper3 command line.&lt;&#x2F;li&gt;
&lt;li&gt;The command is &quot;pepper3&quot;, never &quot;pepper&quot;. If a new, incompatible version comes out, it will be called &quot;pepper4&quot;, and they will be parallel-installable, with no confusion.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Deleting commits from the git history</title>
          <pubDate>Tue, 06 Feb 2018 13:30:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/02/06/deleting-commits-from-the-git-history/</link>
          <guid>https://artificialworlds.net/blog/2018/02/06/deleting-commits-from-the-git-history/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/02/06/deleting-commits-from-the-git-history/">&lt;p&gt;Today I wanted to fix a Git repo that contained some bad commits (i.e. git fsck complained about them). [I wanted to do this because GitLab was not allowing me to push the bad commits.]&lt;&#x2F;p&gt;
&lt;p&gt;I wanted the code to look exactly as it did before, but the history to look different, so the bad commits disappeared, and (presumably) the work done in the bad commits to look like it was done in the commits following them.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I ran:&lt;&#x2F;p&gt;
&lt;pre&gt;git filter-branch -f --commit-filter &#x27;

    if [ &quot;${GIT_COMMIT}&quot; = &quot;abdcef012345abcdef012345etcetcetc&quot; ];
    then
        echo &quot;Skipping GIT_COMMIT=${GIT_COMMIT}&quot; &amp;gt;&amp;amp;2;
        skip_commit &quot;$@&quot;;
    else
        git commit-tree &quot;$@&quot;;
    fi
&#x27; --tag-name-filter cat -- --all
&lt;&#x2F;pre&gt;
&lt;p&gt;(Where abdcef012345abcdef012345etcetcetc was the ID of the commit I wanted to delete.)&lt;&#x2F;p&gt;
&lt;p&gt;Of course, you can make this cleverer to exclude multiple commits at a time, or run this several times, putting in the right commit ID each time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Questions and answers about Pepper3</title>
          <pubDate>Mon, 22 Jan 2018 09:58:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/01/22/questions-and-answers-about-pepper3/</link>
          <guid>https://artificialworlds.net/blog/2018/01/22/questions-and-answers-about-pepper3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/01/22/questions-and-answers-about-pepper3/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;01&#x2F;19&#x2F;examples-of-pepper3-code&#x2F;&quot;&gt;Examples&lt;&#x2F;a&gt;, Questions&lt;&#x2F;p&gt;
&lt;p&gt;My last post &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;01&#x2F;19&#x2F;examples-of-pepper3-code&#x2F;&quot;&gt;Examples of Pepper3 code&lt;&#x2F;a&gt; was a reply to my friend&#x27;s email asking what it was all about. They replied with some questions, and I thought the questions and answers might shed some more light:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Questions!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Brilliant ones, thanks.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In general though you&#x27;ve said a lot about what Pepper can do without giving design decisions.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yep, total brain dump.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Remind me again who this language is for :)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It&#x27;s a multi-paradigm (generic, functional, OO) language aimed at application programmers who want:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;native&quot; performance on their chosen platform (definitely including actual native machine code). This is inspired by C++.&lt;&#x2F;li&gt;
&lt;li&gt;easy deployment (preferably a single binary containing everything, with an option to link most dependencies statically), including packaging of installers for major OSes. This is inspired by C++, and the pain of C++.&lt;&#x2F;li&gt;
&lt;li&gt;perfect flexibility for creating types - &quot;meta-programming&quot; is just programming. Things you would have done using code generation (e.g. generating a class hierarchy from an XSD) are done by running arbitrary code at compile time. The powerful type system is inspired by Haskell and the book &quot;Modern C++ Design&quot;, and the meta-programming is inspired by Lisp.&lt;&#x2F;li&gt;
&lt;li&gt;Simple memory management without GC through ownership. This is inspired by modern C++, and then Rust came along and implemented it before I could, thus proving it works. However, I would remove a lot of the functionality in Rust (lifetimes) to make it much simpler.&lt;&#x2F;li&gt;
&lt;li&gt;Strong support for functional programming if you want it. This is inspired by Haskell.&lt;&#x2F;li&gt;
&lt;li&gt;The simplest possible core language, with application programmers able to expand it by giving them the same tools as the language designers - e.g. &quot;for&quot; is just a function, so you can make your own. I am hoping I can even make &quot;class&quot; a function. This is inspired by Lisp, and oppositely-inspired by Java.&lt;&#x2F;li&gt;
&lt;li&gt;Separation between the idea of Interfaces, which I think I will call &quot;type specifiers&quot; (and will allow arbitrary code execution to determine whether a type satisfies the requirements) and structs&#x2F;classes, allowing us to make new Interfaces and have old code satisfy them, meaning we can do generic stuff with e.g. ints even if no-one declared that &quot;class Int : public Quaternion&quot; or whatever.&lt;&#x2F;li&gt;
&lt;li&gt;Lots of &quot;nudges&quot; towards things that are good: by default things will be functional and immutable - you will have to explicitly say if you want to use more dangerous constructs like side effects and mutable values.&lt;&#x2F;li&gt;
&lt;li&gt;No implicit conversions, or really anything happening without you saying so.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Can you assign floats to ints or vice versa?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, but you shouldn&#x27;t.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;re setting types in code at the start of a file, is this only available in the main file? Are there multiple files per program? Can
you have libraries? If so, do these decide the functionality of their types in the library or does this only happen in the main file?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I haven&#x27;t totally decided - either by being enforced, or as a matter of style, you will generally do this once at the beginning of the program (and choose on the compiler command line to do it e.g. the debug way or the release way) and it will affect all of your code.&lt;&#x2F;p&gt;
&lt;p&gt;Libraries will be packaged as Pepper3 source code, so choices you make of the type of Int etc. will be reflected through the whole dependency tree. Cool, huh?&lt;&#x2F;p&gt;
&lt;p&gt;This is inspired by Python.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can you group variables together into structs or similar?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes - it will be especially easy to make &quot;value types&quot;, and lots of default methods will be provided, that you will be strongly encouraged to use - e.g. copy and move operations. This is inspired by Elm.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why are variables immutable by default but mutable with a special syntax? It&#x27;s the opposite of C++ const, but why that way around?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is one of the &quot;nudges&quot; - immutable stuff is much easier to think about, and makes parallel stuff easier, and allows optimisations and so on, so turning it on by default means you have to choose to take the bad path, and are inclined to take the virtuous one. This is inspired by Haskell and Rust.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why only allow assignments, function calls and operators? I&#x27;m sure you have good reasons.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;To be as simple as possible, so you only have those things to learn and the rest can be understood by just reading the code. This is inspired by Python.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote more of my (earlier) thoughts in this 4-post series, which is better thought through: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;02&#x2F;10&#x2F;goodness-in-programming-languages-part-1-syntax-and-layout&#x2F;&quot;&gt;Goodness in Programming Languages&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Examples of Pepper3 code</title>
          <pubDate>Fri, 19 Jan 2018 12:08:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/01/19/examples-of-pepper3-code/</link>
          <guid>https://artificialworlds.net/blog/2018/01/19/examples-of-pepper3-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/01/19/examples-of-pepper3-code/">&lt;p&gt;Series: Examples, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;01&#x2F;22&#x2F;questions-and-answers-about-pepper3&#x2F;&quot;&gt;Questions&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I have restarted my effort to make a new programming language that fits the way I like things. I haven&#x27;t pushed any code yet, but I have made a lot of progress in my head to understand what I want.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some random examples that might get across some of the ways I am thinking:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; You code using general types like &quot;Int&quot; but you can set what
&#x2F;&#x2F; they really are in the code (usually at the beginning), so
&#x2F;&#x2F; if you plan to use native ints in the production code, it&#x27;s
&#x2F;&#x2F; a good idea to use:
Int = CheckedNativeInt;
&#x2F;&#x2F; while in dev, since it will crash at runtime if you overflow.

&#x2F;&#x2F; Then, in production when you&#x27;re sure you have no errors,
&#x2F;&#x2F; switch to an unchecked one:
Int = NativeInt;

&#x2F;&#x2F; But, if you prefer correctness over efficiency, you can use
&#x2F;&#x2F; mathematical integer that never overflows:
Int = ArbitrarySizeInt;

&#x2F;&#x2F; Variables are immutable by default, so:
Int x = 4;
x = 3;      &#x2F;&#x2F; this is a compile error

&#x2F;&#x2F; But this is OK
Mutable(Int) y = 6;
y = y + x;

&#x2F;&#x2F; Notice that you can call functions that return types that you
&#x2F;&#x2F; then use, like Mutable(Int) here.

&#x2F;&#x2F; Generally, code can run at either compile time or run time.
&#x2F;&#x2F; Code to do with types has to run at compile time.
&#x2F;&#x2F; By default, other code runs at run time, but you can force
&#x2F;&#x2F; it to run early if you want to.

&#x2F;&#x2F; A main method looks like this - you get hold of e.g. stdout through
&#x2F;&#x2F; a World instance - I try to avoid any global functions like print, or
&#x2F;&#x2F; global variables like sys.stdout.

Auto main =
{:(World world)-&amp;gt;Int
    &#x2F;&#x2F;...
};

&#x2F;&#x2F; (Although note that Int, String etc. actually are global variables,
&#x2F;&#x2F; which is a bit annoying)

&#x2F;&#x2F; I wish the main method were simpler-looking.  The only saving grace
&#x2F;&#x2F; is that for simple examples you don&#x27;t need a main method -
&#x2F;&#x2F; Pepper3 just calculates the expression you provide in your file and
&#x2F;&#x2F; prints it out.

&#x2F;&#x2F; Expressions in curly brackets are lambda functions, so:

{3};

&#x2F;&#x2F; is a function taking no arguments, returning 3, and:

{:(Int x)
    x * 2
};

&#x2F;&#x2F; is a function that doubles a value.

Obviously, we can tie functions to names:

Auto dbl =
    {:(Int x)
        x * 2
    };

&#x2F;&#x2F; Meaning we can call dbl like this:
dbl(4);

&#x2F;&#x2F; Auto is a magic word to say (&quot;use type inference&quot;), so
&#x2F;&#x2F; this is equivalent to the above:

fn([Int]-&amp;gt;Int) dbl =
    {:(Int x)
        x * 2
    };

&#x2F;&#x2F; Because {} makes an anon function, things like &quot;for&quot; can be
&#x2F;&#x2F; functions instead of keywords.

for(range(3), {:(Int x)
    world.stdout.println(to(String)(x));
});

&#x2F;&#x2F; As far as possible, Pepper3 will only contain assignment statements:
String s = &quot;xx&quot;;

&#x2F;&#x2F; and expressions containing function calls and operators:
dbl(3) + 6;

&#x2F;&#x2F; This means we can make our own constructs like a different type of
&#x2F;&#x2F; for loop, which would need a new keyword in some languages:

Auto parallel_for = import(multiprocess.parallel_for);
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Recording gameplay videos on RetroPie</title>
          <pubDate>Sun, 07 Jan 2018 18:21:42 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2018/01/07/recording-gameplay-videos-on-retropie/</link>
          <guid>https://artificialworlds.net/blog/2018/01/07/recording-gameplay-videos-on-retropie/</guid>
          <description xml:base="https://artificialworlds.net/blog/2018/01/07/recording-gameplay-videos-on-retropie/">&lt;p&gt;&lt;strong&gt;Stop!&lt;&#x2F;strong&gt; This blog post is out of date - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&#x2F;src&#x2F;branch&#x2F;master&#x2F;instructions.md&quot;&gt;Recording gameplay videos on RetroPie&lt;&#x2F;a&gt; for a live version in codeberg that may be more helpful.&lt;&#x2F;p&gt;
&lt;p&gt;Credits: this is a slightly corrected and shortened version of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;selsine.com&#x2F;2016&#x2F;10&#x2F;15&#x2F;how-to-record-a-game-play-video-from-a-retropie&#x2F;&quot;&gt;How To Record A GamePlay Video From A RetroPie&lt;&#x2F;a&gt; by selsine, which is itself based on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;retroresolution.com&#x2F;2016&#x2F;07&#x2F;06&#x2F;recording-live-gameplay-in-retropies-retroarch-emulators-natively-on-the-raspberry-pi&#x2F;&quot;&gt;Recording Live Gameplay in RetroPieâ€™s RetroArch Emulators Natively on the Raspberry Pi&lt;&#x2F;a&gt; by Retro Resolution.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;retropie.org.uk&#x2F;&quot;&gt;RetroPie&lt;&#x2F;a&gt; is based on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.retroarch.com&#x2F;&quot;&gt;RetroArch&lt;&#x2F;a&gt;. RetroArch has a feature to record gameplay videos, but the current version of RetroPie has it disabled, presumably because it was thought to be too intensive to run properly on a Raspberry Pi.&lt;&#x2F;p&gt;
&lt;p&gt;These instructions tell you how to turn the recording feature on, and set it up. This works perfectly on my Raspberry Pi 3, allowing me to record video and sound from games I am playing.&lt;&#x2F;p&gt;
&lt;p&gt;The code for this is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&quot;&gt;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&lt;&#x2F;a&gt; - this was code written by RetroRevolution, with small corrections and additions by me.&lt;&#x2F;p&gt;
&lt;p&gt;Before you start, you should have RetroPie working and connected to the Internet, and updated to the latest version.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note: you should make a backup of your RetroPie before you start, because if you type the command below you could completely break it, meaning you will have to wipe your SD card and start fresh.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;turning-on-the-recording-feature&quot;&gt;Turning on the recording feature&lt;&#x2F;h2&gt;
&lt;p&gt;RetroArch uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ffmpeg.org&#x2F;&quot;&gt;ffmpeg&lt;&#x2F;a&gt; program to record video. To turn on recording, we need to log into the Pi using ssh, download and compile ffmpeg, and then recompile RetroArch with recording support turned on.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;log-in-to-the-pi-using-ssh&quot;&gt;Log in to the Pi using ssh&lt;&#x2F;h3&gt;
&lt;p&gt;Find out the IP address of your Pi by choosing &quot;RetroPie setup&quot; in the RetroPie menu and choosing &quot;Show IP Address&quot;. Write down the IP address (four numbers with dots in between - for example: &lt;strong&gt;192.168.0.3&lt;&#x2F;strong&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;On your Linux* computer open a Terminal and type:&lt;&#x2F;p&gt;
&lt;pre&gt;ssh pi@192.168.0.3&lt;&#x2F;pre&gt;
&lt;p&gt;(put in the IP address you wrote down instead of 192.168.0.3)&lt;&#x2F;p&gt;
&lt;p&gt;When it asks for your password, type: &lt;strong&gt;raspberry&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If this works right, you should see something like this:&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;retro-login.png&quot; alt=&quot;The RetroPie Project joystick logo&quot; width=&quot;676&quot; height=&quot;378&quot; class=&quot;alignnone size-full wp-image-2331&quot;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;* Note: if you don&#x27;t have Linux, this should work OK on a Mac, or on Windows you could try using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.chiark.greenend.org.uk&#x2F;~sgtatham&#x2F;putty&#x2F;latest.html&quot;&gt;PuTTY&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;download-and-compile-ffmpeg&quot;&gt;Download and compile ffmpeg&lt;&#x2F;h3&gt;
&lt;p&gt;Log in to the RetroPie as described above. The commands shown below should all be typed in to the window where you are logged in to the RetroPie.&lt;&#x2F;p&gt;
&lt;p&gt;Download the script ffmpeg-install.sh by typing this:&lt;&#x2F;p&gt;
&lt;pre&gt;wget https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&#x2F;raw&#x2F;branch&#x2F;master&#x2F;ffmpeg-install.sh&lt;&#x2F;pre&gt;
&lt;p&gt;Now run it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;bash ffmpeg-install.sh&lt;&#x2F;pre&gt;
&lt;p&gt;(Note: DON&#x27;T use sudo to run this - just type exactly what is written above.)&lt;&#x2F;p&gt;
&lt;p&gt;Now wait a long time for this to work. If it prints out errors, something went wrong - read what it says, and you may need to edit the ffmpeg-install.sh script to figure out what to do. Leave a comment and include the errors you saw if you need help.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully it will end successfully and print:&lt;&#x2F;p&gt;
&lt;pre&gt;FFmpeg and Codec Installation Complete&lt;&#x2F;pre&gt;
&lt;p&gt;If so, you are ready to move on to recompiling RetroArch:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;recompile-retroarch-with-recording-turned-on&quot;&gt;Recompile RetroArch with recording turned on&lt;&#x2F;h3&gt;
&lt;p&gt;Download the script build-retroarch-with-ffmpeg.sh by typing this:&lt;&#x2F;p&gt;
&lt;pre&gt;wget https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&#x2F;raw&#x2F;branch&#x2F;master&#x2F;build-retroarch-with-ffmpeg.sh&lt;&#x2F;pre&gt;
&lt;p&gt;Now run it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;bash build-retroarch-with-ffmpeg.sh&lt;&#x2F;pre&gt;
&lt;p&gt;It should finish in about 10 minutes, and print:&lt;&#x2F;p&gt;
&lt;pre&gt;Building RetroArch with ffmpeg enabled complete&lt;&#x2F;pre&gt;
&lt;p&gt;If it printed that, your RetroPie now has recording support! Restart your RetroPie:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;restart-the-retropie&quot;&gt;Restart the RetroPie&lt;&#x2F;h3&gt;
&lt;p&gt;Restart your RetroPie.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to check that recording support is enabled, Look for &quot;Checking FFmpeg Has Been Enabled in RetroArch&quot; on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;retroresolution.com&#x2F;2016&#x2F;07&#x2F;06&#x2F;recording-live-gameplay-in-retropies-retroarch-emulators-natively-on-the-raspberry-pi&#x2F;#li_rgui_check_recording&quot;&gt;RetroResolution guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now you need to set up RetroPie to record your emulator.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-recording-for-your-emulator&quot;&gt;Setting up recording for your emulator&lt;&#x2F;h2&gt;
&lt;p&gt;To set up an emulator, you need a general recording config file (the same for all emulators), and a launch config for the actual emulator you are using.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;create-the-recording-config-file&quot;&gt;Create the recording config file&lt;&#x2F;h3&gt;
&lt;p&gt;Log into the RetroPie as described in the first section, and type this to download the recording config file. If you want to change settings like what file format to record in, this is the file you will need to change.&lt;&#x2F;p&gt;
&lt;pre&gt;wget https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;retropie-recording&#x2F;blob&#x2F;master&#x2F;recording-config.cfg&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;create-a-launch-config-for-your-emulator&quot;&gt;Create a launch config for your emulator&lt;&#x2F;h3&gt;
&lt;p&gt;Each RetroPie emulator has a config file that describes how to launch it. For example, the NES emulator&#x27;s version is in &#x2F;opt&#x2F;retropie&#x2F;configs&#x2F;nes&#x2F;emulators.cfg.&lt;&#x2F;p&gt;
&lt;p&gt;To get a list of all the emulators, log into your RetroPie and type:&lt;&#x2F;p&gt;
&lt;pre&gt;ls &#x2F;opt&#x2F;retropie&#x2F;configs&lt;&#x2F;pre&gt;
&lt;p&gt;In that list you will see, for example, &quot;nes&quot; for the NES emulators, and &quot;gb&quot; for the GameBoy emulators. Find the one you want to edit, and edit it with the nano editor by typing:&lt;&#x2F;p&gt;
&lt;pre&gt;nano &#x2F;opt&#x2F;retropie&#x2F;configs&#x2F;gb&#x2F;emulators.cfg&lt;&#x2F;pre&gt;
&lt;p&gt;(Instead of &quot;gb&quot; type the right name for the emulator you want to use, from the list you got when you typed the &quot;ls&quot; command above.)&lt;&#x2F;p&gt;
&lt;p&gt;Now you need to add a new line in this file. Each line describes how to launch an emulator. You should copy an existing line, and add some more stuff to the end.&lt;&#x2F;p&gt;
&lt;p&gt;For example, my version of this file looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;lr-gambatte = &quot;&#x2F;opt&#x2F;retropie&#x2F;emulators&#x2F;retroarch&#x2F;bin&#x2F;retroarch -L &#x2F;opt&#x2F;retropie&#x2F;libretrocores&#x2F;lr-gambatte&#x2F;gambatte_libretro.so --config &#x2F;opt&#x2F;retropie&#x2F;configs&#x2F;gb&#x2F;retroarch.cfg %ROM%&quot;
&lt;span style=&quot;color: green;&quot;&gt;lr-gambatte&lt;&#x2F;span&gt;&lt;span style=&quot;color: red;&quot;&gt;-record&lt;&#x2F;span&gt;&lt;span style=&quot;color: green;&quot;&gt; = &quot;&#x2F;opt&#x2F;retropie&#x2F;emulators&#x2F;retroarch&#x2F;bin&#x2F;retroarch -L &#x2F;opt&#x2F;retropie&#x2F;libretrocores&#x2F;lr-gambatte&#x2F;gambatte_libretro.so --config &#x2F;opt&#x2F;retropie&#x2F;configs&#x2F;gb&#x2F;retroarch.cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: red;&quot;&gt; --record &#x2F;home&#x2F;pi&#x2F;recording_GB_$(date +%Y-%m-%d-%H%M%S).mkv --recordconfig &#x2F;home&#x2F;pi&#x2F;recording-config.cfg&lt;&#x2F;span&gt;&lt;span style=&quot;color: green;&quot;&gt; %ROM%&quot;&lt;&#x2F;span&gt;
default = &quot;lr-gambatte&quot;
lr-tgbdual = &quot;&#x2F;opt&#x2F;retropie&#x2F;emulators&#x2F;retroarch&#x2F;bin&#x2F;retroarch -L &#x2F;opt&#x2F;retropie&#x2F;libretrocores&#x2F;lr-tgbdual&#x2F;tgbdual_libretro.so --config &#x2F;opt&#x2F;retropie&#x2F;configs&#x2F;gb&#x2F;retroarch.cfg %ROM%&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;The line I added is coloured: The green parts are things copied from the line above, and the red parts are new - those parts tell the launcher to use the recording config we made in the previous section.&lt;&#x2F;p&gt;
&lt;p&gt;When you&#x27;ve made your edits, press Ctrl-X to exit nano, and type &quot;Y&quot; when it asks whether you want to save.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;ve done something similar to this for every emulator you want to record with, you are ready to actually do the recording!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;actually-doing-a-recording&quot;&gt;Actually doing a recording&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;launching-a-game-with-recording-turned-on&quot;&gt;Launching a game with recording turned on&lt;&#x2F;h3&gt;
&lt;p&gt;In the normal RetroPie interface, go to your emulator and start it, but press the A button while it&#x27;s launching, and choose &quot;Select emulator for ROM&quot;. In the list that comes up, choose the new line you added in emulators.cfg. In our example, that was called &quot;lr-gambatte-record&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Now play the game, and exit when you are finished. If all goes well, the recording will have been saved!&lt;&#x2F;p&gt;
&lt;p&gt;(Note: doing this means that every time you launch this game it will be recorded. To stop it doing this, press the &quot;A&quot; button while it&#x27;s launching, choose &quot;Select emulator for ROM&quot; and choose the normal line - in our example that would be &quot;lr-gambatte&quot;.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-the-recorded-files&quot;&gt;Getting the recorded files&lt;&#x2F;h3&gt;
&lt;p&gt;To get your recording off the RetroPie, go back to your computer, open a terminal, and type:&lt;&#x2F;p&gt;
&lt;pre&gt;scp pi@192.168.0.3:recording_*.mkv .&#x2F;&lt;&#x2F;pre&gt;
&lt;p&gt;This will copy all recorded videos from your RetroPie onto your computer (into your home directory, unless you did a cd commmand before you typed the above).&lt;&#x2F;p&gt;
&lt;p&gt;Now you should delete the files from your RetroPie. Log in to the RetroPie as described in the first section, and delete all recording files by typing this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Note: This deletes all your recordings, and you can&#x27;t undo!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;pre&gt;rm recording_*.mkv&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Note: This deletes all your recordings, and you can&#x27;t undo!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;safer-recording-onto-a-usb-stick&quot;&gt;Safer: recording onto a USB stick&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;&#x2F;strong&gt;: recording directly onto the RetroPie like we described above is dangerous because you could fill up all the disk space or corrupt your SD card, which could make RetroPie stop working, meaning you need to wipe your SD card and set up RetroPie again.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s safer to record onto a separate USB disk. To find out how, read &quot;Recording to an External Storage Device&quot; in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;retroresolution.com&#x2F;2016&#x2F;07&#x2F;06&#x2F;recording-live-gameplay-in-retropies-retroarch-emulators-natively-on-the-raspberry-pi&#x2F;#li_performance_external_hdd&quot;&gt;Retro Resolution&#x27;s guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Steve
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, thanks for putting this together:)&lt;&#x2F;p&gt;
&lt;p&gt;Step 2 is failing for me though. There seems to be a problem with the line number reference in &#x2F;retroarch.sh but I can’t figure it out.&lt;&#x2F;p&gt;
&lt;p&gt;When I launch  build-retroarch-with-ffmpeg.sh, the script works up to this point before failing:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;echo ‘=== Patching RetroArch code to enable ffmpeg ===’&lt;br&#x2F;&gt;
=== Patching RetroArch code to enable ffmpeg ===&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;cd RetroPie-Setup&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;patch -p1&lt;br&#x2F;&gt;
patching file scriptmodules&#x2F;emulators&#x2F;retroarch.sh&lt;br&#x2F;&gt;
Hunk #1 FAILED at 40.&lt;br&#x2F;&gt;
1 out of 1 hunk FAILED — saving rejects to file scriptmodules&#x2F;emulators&#x2F;retroarch.sh.rej&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In my retroarch.sh file, &quot;function build_retroarch&quot; is actually on line 41. As a test I tried to bring everything up by one line but that didn’t work either.&lt;&#x2F;p&gt;
&lt;p&gt;Any ideas?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Ah, sorry to hear this.  I am guessing the retroarch source code has been updated so my patch file doesn’t apply any more.  I will need to update it when I get time, or if someone else can do it they should send me a merge request on GitHub.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Steve
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey, I did a bit more digging and went back to retroresolution’s original how to. After running sudo .&#x2F;retropie_packages.sh retroarch (to rebuild retroarch after removing the disable-ffmpeg flag), the script fails with:&lt;br&#x2F;&gt;
&quot;Could not successfully build retroarch – RetroArch – frontend to the libretro emulator cores – required by all lr-* emulators (&#x2F;root&#x2F;RetroPie-Setup&#x2F;tmp&#x2F;build&#x2F;retroarch&#x2F;retroarch not found).&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Found that some other people had the same issue but no solution was found. I suspect that the issue isn’t with your script so I think I’ll just give it another whirl with a fresh installation. I’ll let you know how it goes :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Good luck Steve, and thanks for the info.  Let us know.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Chris
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Have you played around with Twitch streaming any from your pi?  I have everything working except for N64 and PSP streaming.  For some reason the game launches but the stream doesn’t start like it does for all other emulators.  I read somewhere that those emulators may use more than the single core the others use, so maybe that has something to do with it?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Chris, no I haven’t tried that – I’m surprised it works at all!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Chris
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;It actually works fairly well. Itâ€™s a similar setup to getting gameplay recording working.&lt;&#x2F;p&gt;
&lt;p&gt;I followed a guide on another blog post I found (&lt;a href=&quot;http:&#x2F;&#x2F;selsine.com&#x2F;2016&#x2F;10&#x2F;16&#x2F;how-to-live-stream-to-twitch-from-a-retropie&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;selsine.com&#x2F;2016&#x2F;10&#x2F;16&#x2F;how-to-live-str...&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Good to hear, thanks for the link!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    partTimeNinja
    &lt;span class=&quot;comment_date&quot;&gt;2018-05-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I had issues running the ffmpeg-install.sh . I was getting &quot;There are problems and -y was used without —force-yes&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Eventually I realized that the original folder I was in when I did step #1 was owned but root so I had to use &quot;sudo wget…&quot; I changed ownership of ffmpeg-install.sh and it seems to be running correctly now.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    partTimeNinja
    &lt;span class=&quot;comment_date&quot;&gt;2018-05-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Never mind, same error.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    salvatore
    &lt;span class=&quot;comment_date&quot;&gt;2019-05-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;and if I wanted to transmit the recording later on twitch what should I do?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nicolas Baumgardt
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi. I followed your instructions, but got the following errors right after start:&lt;&#x2F;p&gt;
&lt;p&gt;After I ran &quot;bash ffmpeg-install.sh&quot; I get this error:&lt;&#x2F;p&gt;
&lt;p&gt;pi@retropie:~ $ bash ffmpeg-install.sh&lt;br&#x2F;&gt;
+ echo ‘Installing FFmpeg Suite’&lt;br&#x2F;&gt;
Installing FFmpeg Suite&lt;&#x2F;p&gt;
&lt;p&gt;+ echo ———————————-&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;echo&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;echo ‘=== Updating APT repo ===’&lt;br&#x2F;&gt;
=== Updating APT repo ===&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;sudo apt-get update&lt;br&#x2F;&gt;
Hit:1 &lt;a href=&quot;http:&#x2F;&#x2F;archive.raspberrypi.org&#x2F;debian&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;archive.raspberrypi.org&#x2F;debian&lt;&#x2F;a&gt; stretch InRelease&lt;br&#x2F;&gt;
Get:2 &lt;a href=&quot;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&lt;&#x2F;a&gt; stretch InRelease [15.0 kB]&lt;br&#x2F;&gt;
Get:3 &lt;a href=&quot;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&lt;&#x2F;a&gt; stretch&#x2F;main armhf Packages [11.7 MB]&lt;br&#x2F;&gt;
Err:3 &lt;a href=&quot;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&lt;&#x2F;a&gt; stretch&#x2F;main armhf Packages&lt;br&#x2F;&gt;
Hash Sum mismatch&lt;br&#x2F;&gt;
Hashes of expected file:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Filesize:11662764 [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA256:d01c7c7b4963cb29fe508f90104ff697f7f68b127e1546e88f7f880098a69d85&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA1:82848ac64aa65d092e7066352ddc5bc99d168135 [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;MD5Sum:c46cdb694218cf7834c00a1493e13f5e [weak]&lt;br&#x2F;&gt;
Hashes of received file:&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA256:3138f9acc731b771b152ceeee83e3e9769784cedfb368e2545472df6d0bb0b0a&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA1:1ed901f11928bd6f5b2a09118f055c70d2fa05ac [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;MD5Sum:f5fcd94cb6aed37c8ac05bd253081fde [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Filesize:1551993 [weak]&lt;br&#x2F;&gt;
Last modification reported: Mon, 05 Aug 2019 23:19:49 +0000&lt;br&#x2F;&gt;
Release file created at: Mon, 05 Aug 2019 23:28:15 +0000&lt;br&#x2F;&gt;
Fetched 1567 kB in 41s (38.1 kB&#x2F;s)&lt;br&#x2F;&gt;
Reading package lists… Done&lt;br&#x2F;&gt;
E: Failed to fetch &lt;a href=&quot;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&#x2F;dists&#x2F;stretch&#x2F;main&#x2F;binary-armhf&#x2F;Packages.xz&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;raspbian.raspberrypi.org&#x2F;raspbian&#x2F;dist...&lt;&#x2F;a&gt;  Hash Sum mismatch&lt;br&#x2F;&gt;
Hashes of expected file:&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Filesize:11662764 [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA256:d01c7c7b4963cb29fe508f90104ff697f7f68b127e1546e88f7f880098a69d85&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA1:82848ac64aa65d092e7066352ddc5bc99d168135 [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;MD5Sum:c46cdb694218cf7834c00a1493e13f5e [weak]&lt;br&#x2F;&gt;
Hashes of received file:&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA256:3138f9acc731b771b152ceeee83e3e9769784cedfb368e2545472df6d0bb0b0a&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;SHA1:1ed901f11928bd6f5b2a09118f055c70d2fa05ac [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;MD5Sum:f5fcd94cb6aed37c8ac05bd253081fde [weak]&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Filesize:1551993 [weak]&lt;br&#x2F;&gt;
Last modification reported: Mon, 05 Aug 2019 23:19:49 +0000&lt;br&#x2F;&gt;
Release file created at: Mon, 05 Aug 2019 23:28:15 +0000&lt;br&#x2F;&gt;
E: Some index files failed to download. They have been ignored, or old ones used instead.&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Then when trying to execute the second script &quot;bash build-retroarch-with-ffmpeg.sh&quot;, I get this error:&lt;&#x2F;p&gt;
&lt;p&gt;pi@retropie:~ $ bash build-retroarch-with-ffmpeg.sh&lt;br&#x2F;&gt;
+ echo ‘Building RetroArch with ffmpeg enabled’&lt;br&#x2F;&gt;
Building RetroArch with ffmpeg enabled&lt;&#x2F;p&gt;
&lt;p&gt;+ echo ———————————————————&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;echo&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;echo ‘=== Downloading patch file ===’&lt;br&#x2F;&gt;
=== Downloading patch file ===&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;wget &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;retropie-recording&#x2F;master&#x2F;enable-ffmpeg.patch&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;...&lt;&#x2F;a&gt;&lt;br&#x2F;&gt;
—2019-08-06 14:52:06—  &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;retropie-recording&#x2F;master&#x2F;enable-ffmpeg.patch&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;...&lt;&#x2F;a&gt;&lt;br&#x2F;&gt;
Resolving raw.githubusercontent.com (raw.githubusercontent.com)… 151.101.240.133&lt;br&#x2F;&gt;
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.240.133|:443… connected.&lt;br&#x2F;&gt;
HTTP request sent, awaiting response… 200 OK&lt;br&#x2F;&gt;
Length: 690 [text&#x2F;plain]&lt;br&#x2F;&gt;
Saving to: ‘enable-ffmpeg.patch’&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;enable-ffmpeg.patch                                  100%[===================================================================================================================&amp;gt;]     690  —.-KB&#x2F;s    in 0s&lt;&#x2F;p&gt;
&lt;p&gt;2019-08-06 14:52:07 (3.95 MB&#x2F;s) – ‘enable-ffmpeg.patch’ saved [&lt;sup&gt;690&lt;&#x2F;sup&gt;⁄&lt;sub&gt;690&lt;&#x2F;sub&gt;]&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;echo ‘=== Patching RetroArch code to enable ffmpeg ===’&lt;br&#x2F;&gt;
=== Patching RetroArch code to enable ffmpeg ===&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;cd RetroPie-Setup&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;patch -p1&lt;br&#x2F;&gt;
patching file scriptmodules&#x2F;emulators&#x2F;retroarch.sh&lt;br&#x2F;&gt;
Hunk #1 FAILED at 40.&lt;br&#x2F;&gt;
1 out of 1 hunk FAILED — saving rejects to file scriptmodules&#x2F;emulators&#x2F;retroarch.sh.rej&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Any help highly appreciated. Thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Nicolas, I’m afraid it looks like my script has gone out of date and I’m afraid I don’t know how to fix it, sorry.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nicolas Baumgardt
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Never mind, thanks for your response! Everything has its lifetime, scripts are no exception ;–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Liam
    &lt;span class=&quot;comment_date&quot;&gt;2020-07-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;i tried to run ffmpeg-install.sh, but halfway through it said &quot;+ git clone git:&#x2F;&#x2F;git.videolan.org&#x2F;x264&lt;br&#x2F;&gt;
Cloning into ‘x264’…&lt;br&#x2F;&gt;
fatal: remote error: access denied or repository not exported: &#x2F;x264&lt;br&#x2F;&gt;
&quot;. what do i do?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-07-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Liam, I don’t know.  It would be worth trying again since it might have been a momentary problem.  However, I think this code is out-of-date now, so if you want to follow this you will need to understand and update it to suit the way things have changed in the meantime.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Yavuz Selim
    &lt;span class=&quot;comment_date&quot;&gt;2020-12-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy:&lt;&#x2F;p&gt;
&lt;p&gt;Change the following line:&lt;br&#x2F;&gt;
git clone git:&#x2F;&#x2F;git.videolan.org&#x2F;x264&lt;&#x2F;p&gt;
&lt;p&gt;To:&lt;br&#x2F;&gt;
git clone &lt;a href=&quot;https:&#x2F;&#x2F;code.videolan.org&#x2F;videolan&#x2F;x264.git&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;code.videolan.org&#x2F;videolan&#x2F;x264.git&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-12-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Yavuz Selim – fix applied in the repo!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Digital404
    &lt;span class=&quot;comment_date&quot;&gt;2024-12-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I tried to bash the build-retroarch-with-ffmpeg.sh and this is the error I get at the end.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Digital404
    &lt;span class=&quot;comment_date&quot;&gt;2024-12-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi there!&lt;&#x2F;p&gt;
&lt;p&gt;I am new here I am trying to add recording to my emulators like what I see on here. I got passed everything beside&lt;br&#x2F;&gt;
&quot;Bash build-retroarch-with-ffmpeg.sh&quot; It giving me error message.&lt;&#x2F;p&gt;
&lt;p&gt;bash build-retroarch-with-ffmpeg.sh&lt;br&#x2F;&gt;
+ echo ‘Building RetroArch with ffmpeg enabled’&lt;br&#x2F;&gt;
Building RetroArch with ffmpeg enabled&lt;&#x2F;p&gt;
&lt;p&gt;+ echo ———————————————————&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;echo&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;echo ‘=== Downloading patch file ===’&lt;br&#x2F;&gt;
=== Downloading patch file ===&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;wget &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;retropie-recording&#x2F;master&#x2F;en&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;...&lt;&#x2F;a&gt;                                                                             able-ffmpeg.patch&lt;br&#x2F;&gt;
—2024-12-26 19:28:57—  &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;retropie-r&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;...&lt;&#x2F;a&gt;                                                                             ecording&#x2F;master&#x2F;enable-ffmpeg.patch&lt;br&#x2F;&gt;
Resolving raw.githubusercontent.com (raw.githubusercontent.com)… 185.199.108.1                                                                             33, 185.199.109.133, 185.199.110.133, …&lt;br&#x2F;&gt;
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.                                                                             133|:443… connected.&lt;br&#x2F;&gt;
HTTP request sent, awaiting response… 200 OK&lt;br&#x2F;&gt;
Length: 690 [text&#x2F;plain]&lt;br&#x2F;&gt;
Saving to: ‘enable-ffmpeg.patch.2’&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;enable-ffmpeg.patch 100%[===================&amp;gt;]     690  —.-KB&#x2F;s    in 0.002s&lt;&#x2F;p&gt;
&lt;p&gt;2024-12-26 19:29:03 (416 KB&#x2F;s) – ‘enable-ffmpeg.patch.2’ saved [&lt;sup&gt;690&lt;&#x2F;sup&gt;⁄&lt;sub&gt;690&lt;&#x2F;sub&gt;]&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;echo ‘=== Patching RetroArch code to enable ffmpeg ===’&lt;br&#x2F;&gt;
=== Patching RetroArch code to enable ffmpeg ===&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;cd RetroPie-Setup&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;patch -p1&lt;br&#x2F;&gt;
patching file scriptmodules&#x2F;emulators&#x2F;retroarch.sh&lt;br&#x2F;&gt;
Hunk #1 FAILED at 40.&lt;br&#x2F;&gt;
1 out of 1 hunk FAILED — saving rejects to file scriptmodules&#x2F;emulators&#x2F;retroar                                                                             ch.sh.rej&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Can someone help me.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>TECH(K)NOW Day workshop on &quot;Writing a programming language&quot;</title>
          <pubDate>Tue, 10 Oct 2017 15:38:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/10/10/techknow-day-workshop-on-writing-a-programming-language/</link>
          <guid>https://artificialworlds.net/blog/2017/10/10/techknow-day-workshop-on-writing-a-programming-language/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/10/10/techknow-day-workshop-on-writing-a-programming-language/">&lt;p&gt;My &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openmarket.com&quot;&gt;OpenMarket&lt;&#x2F;a&gt; colleagues and I ran a workshop at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;techknowday&quot;&gt;TECH(K)NOW Day&lt;&#x2F;a&gt; on how to write your own programming language:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;techknowday-workshop.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A big thank you to my colleagues from OpenMarket who volunteered to help: Rowan, Jenny, Zach, James and Elliot.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;om-volunteers.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;An extra thank you to Zach and Elliott for their impromptu help on the information desk for attendees:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;om-elliott.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully the attendees enjoyed it and learned a bit:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Making own programming language &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;TECHKNOWDay?ref_src=twsrc%5Etfw&quot;&gt;@TECHKNOWDay&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;t.co&#x2F;jfUFHq7iO3&quot;&gt;pic.twitter.com&#x2F;jfUFHq7iO3&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— 4theLoveOfCode (@4theLoveOfCode) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;4theLoveOfCode&#x2F;status&#x2F;917736074297962496?ref_src=twsrc%5Etfw&quot;&gt;October 10, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;It was an amazing workshop thank you &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam?ref_src=twsrc%5Etfw&quot;&gt;@andybalaam&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— 4theLoveOfCode (@4theLoveOfCode) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;4theLoveOfCode&#x2F;status&#x2F;917834533118140417?ref_src=twsrc%5Etfw&quot;&gt;October 10, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;You can find the workshop slides, the full code, info about another simple language called Cell, and lots more links here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;videos-write-your-own-language&quot;&gt;github.com&#x2F;andybalaam&#x2F;videos-write-your-own-language&lt;&#x2F;a&gt;, my blog at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&quot;&gt;artificialworlds.net&#x2F;blog&lt;&#x2F;a&gt;, and follow me on twitter &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;@andybalaam&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to OpenMarket for supporting us in running this workshop!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;At &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;openmarket?ref_src=twsrc%5Etfw&quot;&gt;@OpenMarket&lt;&#x2F;a&gt; we are honoured to support the excellent work of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WomenWhoCode?ref_src=twsrc%5Etfw&quot;&gt;@WomenWhoCode&lt;&#x2F;a&gt;â€™s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;TECHKNOWDay?ref_src=twsrc%5Etfw&quot;&gt;@TECHKNOWDay&lt;&#x2F;a&gt; and celebrate &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;AdaLovelaceDay?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#AdaLovelaceDay&lt;&#x2F;a&gt; &amp;amp; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;hashtag&#x2F;womenintech?src=hash&amp;amp;ref_src=twsrc%5Etfw&quot;&gt;#womenintech&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;t.co&#x2F;udYXTLjOqL&quot;&gt;https:&#x2F;&#x2F;t.co&#x2F;udYXTLjOqL&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— OpenMarket (@openmarket) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;openmarket&#x2F;status&#x2F;917710403815297024?ref_src=twsrc%5Etfw&quot;&gt;October 10, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
      <item>
          <title>FileZilla with proxy not working with strange characters in the password</title>
          <pubDate>Fri, 29 Sep 2017 12:35:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/09/29/filezilla-with-proxy-not-working-with-strange-characters-in-the-password/</link>
          <guid>https://artificialworlds.net/blog/2017/09/29/filezilla-with-proxy-not-working-with-strange-characters-in-the-password/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/09/29/filezilla-with-proxy-not-working-with-strange-characters-in-the-password/">&lt;p&gt;Today I found I could not connect out through my proxy server with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;filezilla-project.org&#x2F;&quot;&gt;FileZilla&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It stopped working when I changed my password to something containing a double quote &quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The solution? Change to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;winscp.net&quot;&gt;WinSCP&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>HTML5 CSS Toolbar + zoomable workspace that is mobile-friendly and adaptive</title>
          <pubDate>Wed, 20 Sep 2017 23:42:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/09/20/html5-css-toolbar-zoomable-workspace-that-is-mobile-friendly-and-adaptive/</link>
          <guid>https://artificialworlds.net/blog/2017/09/20/html5-css-toolbar-zoomable-workspace-that-is-mobile-friendly-and-adaptive/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/09/20/html5-css-toolbar-zoomable-workspace-that-is-mobile-friendly-and-adaptive/">&lt;p&gt;I have been working on a prototype &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;level-editor&quot;&gt;level editor&lt;&#x2F;a&gt; for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, and I&#x27;ve had trouble getting the layout I wanted: a toolbar at the side or top of the screen, and the rest a zoomable workspace.&lt;&#x2F;p&gt;
&lt;p&gt;Something like this is very common in many desktop applications, but not that easy to achieve in a web page, especially because we want to take care that it adapts to different screen sizes and orientations, and, for example, allows zooming the toolbar buttons in case we find ourselves on a device with different resolution from what we were expecting.&lt;&#x2F;p&gt;
&lt;p&gt;In the end I&#x27;ve gone with a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;CSS_Grid_Layout&quot;&gt;grid-layout&lt;&#x2F;a&gt; solution and accepted the fact that sometimes on mobile devices when I zoom in my toolbar will disappear off the top&#x2F;side. When I scroll back to it, it stays around, so using this setup is quite natural. On the desktop, it works how you&#x27;d expect, with the toolbar staying on screen at all zoom levels.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how it looks on a landscape display:&lt;&#x2F;p&gt;
&lt;iframe width=&quot;400&quot; height=&quot;200&quot; src=&quot;&#x2F;toolbar&quot; style=&quot;border: 3px inset;&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;and portrait:&lt;&#x2F;p&gt;
&lt;iframe width=&quot;200&quot; height=&quot;400&quot; src=&quot;&#x2F;toolbar&quot; style=&quot;border: 3px inset;&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Read the full &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;toolbar&#x2F;index.html.txt&quot;&gt;source code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As you can see from the code linked above, after much fiddling I managed to achieve this with a relatively small amount of CSS, and no JavaScript. I&#x27;m hoping it will behave well in unexpected scenarios, because the code expresses what I want fairly closely.&lt;&#x2F;p&gt;
&lt;p&gt;The important bits of the HTML are simple - a main div, a toolbar containing buttons, and a workspace containing some kind of work:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;div id=&quot;main&quot;&amp;gt;
    &amp;lt;div id=&quot;toolbar&quot;&amp;gt;
        &amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;&amp;lt;button&amp;gt;&amp;lt;&#x2F;button&amp;gt;
    &amp;lt;&#x2F;div&amp;gt;
    &amp;lt;div id=&quot;workspace&quot;&amp;gt;
        &amp;lt;div id=&quot;work&quot;&amp;gt;
        &amp;lt;&#x2F;div&amp;gt;
    &amp;lt;&#x2F;div&amp;gt;
&amp;lt;&#x2F;div&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;The keys bits of the CSS are:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;* Ensure we take up the full height of the page. *&#x2F;
html, body, #main
{
    height: 100%;
}

@media all and (orientation:landscape)
{
    &#x2F;* On a wide screen, it&#x27;s a grid with 2 columns,
       and the toolbar can scroll downwards. *&#x2F;
    #main
    {
        display: grid;
        grid-template-columns: 5em 1fr;
    }
    #toolbar
    {
        overflow-x: hidden;
        overflow-y: auto;
    }
}

@media all and (orientation:portrait)
{
    &#x2F;* On a tall screen, it&#x27;s a grid with 2 rows,
       and the toolbar can scroll right. *&#x2F;
    #main
    {
        display: grid;
        grid-template-rows: 5em 1fr;
    }
    #toolbar
    {
        overflow-x: auto;
        overflow-y: hidden;
        white-space: nowrap;
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;That replaces an awful lot of code in my first attempt, so I&#x27;m reasonably happy. If anyone has suggestions about how to make &quot;100%&quot; really mean 100% of the real device width and height, let me know. If I do some JavaScript I can make Mobile Firefox fit to the real screen size, but Mobile Chrome (and, I assume, Mobile Safari) lie to me about the screen size when zoomed in.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>maven-assembly-plugin descriptor for a simple tarball with dependencies</title>
          <pubDate>Thu, 14 Sep 2017 13:06:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/09/14/maven-assembly-plugin-descriptor-for-a-simple-tarball-with-dependencies/</link>
          <guid>https://artificialworlds.net/blog/2017/09/14/maven-assembly-plugin-descriptor-for-a-simple-tarball-with-dependencies/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/09/14/maven-assembly-plugin-descriptor-for-a-simple-tarball-with-dependencies/">&lt;p&gt;Today I was trying to make a simple tarball of a project + its dependent jar using the maven-assembly-plugin. I know this is a terrible way to do anything, but hey, just in case someone else wants to do something just as terrible, here are my pom.xml and assembly.xml (the assembly descriptor):&lt;&#x2F;p&gt;
&lt;pre&gt;$ tree
.
â”œâ”€â”€ assembly.xml
â”œâ”€â”€ pom.xml
â””â”€â”€ src
    â””â”€â”€ main
        â””â”€â”€ java
            â””â”€â”€ AssemblyExample.java&lt;&#x2F;pre&gt;
&lt;pre&gt;$ cat pom.xml
&amp;lt;project
    xmlns=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;POM&#x2F;4.0.0&quot;
    xmlns:xsi=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2001&#x2F;XMLSchema-instance&quot;
    xsi:schemaLocation=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;POM&#x2F;4.0.0 http:&#x2F;&#x2F;maven.apache.org&#x2F;maven-v4_0_0.xsd&quot;
&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;&#x2F;modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.example&amp;lt;&#x2F;groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;myproject&amp;lt;&#x2F;artifactId&amp;gt;
    &amp;lt;name&amp;gt;My Projects&amp;lt;&#x2F;name&amp;gt;
    &amp;lt;version&amp;gt;0.1&amp;lt;&#x2F;version&amp;gt;
    &amp;lt;build&amp;gt;&amp;lt;plugins&amp;gt;&amp;lt;plugin&amp;gt;
        &amp;lt;artifactId&amp;gt;maven-assembly-plugin&amp;lt;&#x2F;artifactId&amp;gt;
        &amp;lt;version&amp;gt;2.2.1&amp;lt;&#x2F;version&amp;gt;
        &amp;lt;configuration&amp;gt;&amp;lt;descriptors&amp;gt;
            &amp;lt;descriptor&amp;gt;assembly.xml&amp;lt;&#x2F;descriptor&amp;gt;
        &amp;lt;&#x2F;descriptors&amp;gt;&amp;lt;&#x2F;configuration&amp;gt;
        &amp;lt;executions&amp;gt; &amp;lt;execution&amp;gt;
            &amp;lt;phase&amp;gt;package&amp;lt;&#x2F;phase&amp;gt;
            &amp;lt;goals&amp;gt;&amp;lt;goal&amp;gt;attached&amp;lt;&#x2F;goal&amp;gt;&amp;lt;&#x2F;goals&amp;gt;
        &amp;lt;&#x2F;execution&amp;gt;&amp;lt;&#x2F;executions&amp;gt;
    &amp;lt;&#x2F;plugin&amp;gt;&amp;lt;&#x2F;plugins&amp;gt;&amp;lt;&#x2F;build&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.slf4j&amp;lt;&#x2F;groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;slf4j-log4j12&amp;lt;&#x2F;artifactId&amp;gt;
            &amp;lt;version&amp;gt;1.7.2&amp;lt;&#x2F;version&amp;gt;
        &amp;lt;&#x2F;dependency&amp;gt;
    &amp;lt;&#x2F;dependencies&amp;gt;
&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ cat assembly.xml
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;assembly&amp;gt;
    &amp;lt;id&amp;gt;ap3&amp;lt;&#x2F;id&amp;gt;

    &amp;lt;formats&amp;gt;
        &amp;lt;format&amp;gt;tar.gz&amp;lt;&#x2F;format&amp;gt;
    &amp;lt;&#x2F;formats&amp;gt;

    &amp;lt;includeBaseDirectory&amp;gt;true&amp;lt;&#x2F;includeBaseDirectory&amp;gt;

    &amp;lt;dependencySets&amp;gt;
        &amp;lt;dependencySet&amp;gt;
            &amp;lt;outputDirectory&amp;gt;jars&amp;lt;&#x2F;outputDirectory&amp;gt;
            &amp;lt;scope&amp;gt;runtime&amp;lt;&#x2F;scope&amp;gt;
        &amp;lt;&#x2F;dependencySet&amp;gt;
    &amp;lt;&#x2F;dependencySets&amp;gt;

&amp;lt;&#x2F;assembly&amp;gt;&lt;&#x2F;pre&gt;
&lt;pre&gt;$ mvn package
...
[INFO] Compiling 1 source file to &#x2F;home&#x2F;andrebal&#x2F;Desktop&#x2F;assemblyexample&#x2F;target&#x2F;classes
[INFO]
...
[INFO] Building jar: &#x2F;home&#x2F;andrebal&#x2F;Desktop&#x2F;assemblyexample&#x2F;target&#x2F;myproject-0.1.jar
...
[INFO] --- maven-assembly-plugin:2.2.1:attached (default) @ myproject ---
[INFO] Reading assembly descriptor: assembly.xml
[INFO] Building tar : &#x2F;home&#x2F;andrebal&#x2F;Desktop&#x2F;assemblyexample&#x2F;target&#x2F;myproject-0.1-ap3.tar.gz
...
[INFO] BUILD SUCCESS&lt;&#x2F;pre&gt;
&lt;pre&gt;$ tar -tzf target&#x2F;myproject-0.1-ap3.tar.gz
myproject-0.1&#x2F;jars&#x2F;slf4j-log4j12-1.7.2.jar
myproject-0.1&#x2F;jars&#x2F;slf4j-api-1.7.2.jar
myproject-0.1&#x2F;jars&#x2F;log4j-1.2.17.jar
myproject-0.1&#x2F;jars&#x2F;myproject-0.1.jar&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Blog aggregator&#x2F;planet in WordPress using FeedWordPress</title>
          <pubDate>Tue, 12 Sep 2017 13:55:34 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/09/12/blog-aggregatorplanet-in-wordpress-using-feedwordpress/</link>
          <guid>https://artificialworlds.net/blog/2017/09/12/blog-aggregatorplanet-in-wordpress-using-feedwordpress/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/09/12/blog-aggregatorplanet-in-wordpress-using-feedwordpress/">&lt;p&gt;I used to run &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;planetcode&#x2F;&quot;&gt;Planet Code&lt;&#x2F;a&gt; using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;rubys&#x2F;venus&quot;&gt;Venus&lt;&#x2F;a&gt; but that started breaking recently with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugs.debian.org&#x2F;cgi-bin&#x2F;bugreport.cgi?bug=813313&quot;&gt;500 errors for SNI-enabled sites&lt;&#x2F;a&gt;, and it looks very unmaintained.&lt;&#x2F;p&gt;
&lt;p&gt;I considered some other static aggregators, but on shared hosting it&#x27;s hard to get the right Ruby version and gems installed and the like, so I went for another &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wordpress.org&#x2F;&quot;&gt;WordPress&lt;&#x2F;a&gt;, and found the very nice &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;feedwordpress.radgeek.com&#x2F;&quot;&gt;FeedWordPress&lt;&#x2F;a&gt; plugin to suck up all the feeds of coders somewhat related to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;&quot;&gt;ACCU&lt;&#x2F;a&gt; and&#x2F;or coding generally.&lt;&#x2F;p&gt;
&lt;p&gt;Let me know what you think.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Adding a day in Python datetimes - use timedelta, not the datetime constructor</title>
          <pubDate>Mon, 07 Aug 2017 08:30:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/08/07/adding-a-day-in-python-datetimes-use-timedelta-not-the-datetime-constructor/</link>
          <guid>https://artificialworlds.net/blog/2017/08/07/adding-a-day-in-python-datetimes-use-timedelta-not-the-datetime-constructor/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/08/07/adding-a-day-in-python-datetimes-use-timedelta-not-the-datetime-constructor/">&lt;p&gt;If you want &quot;tomorrow&quot; in Python datetimes, don&#x27;t construct a datetime like this:&lt;&#x2F;p&gt;
&lt;pre&gt;from datetime import datetime, timedelta
td = datetime.today()
tm1 = datetime(td.year, td.month, &lt;span style=&quot;color: red;&quot;&gt;td.day + 1&lt;&#x2F;span&gt;, 14, 0, 0)
&lt;span style=&quot;color: red;&quot;&gt;# Don&#x27;t do this!&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Because it will work sometimes, but fail when today is the last day of the month:&lt;&#x2F;p&gt;
&lt;pre&gt;Traceback (most recent call last):
  File &quot;.&#x2F;tomorrow&quot;, line 6, in &lt;module&gt;
    tm1 = datetime(td.year, td.month, td.day + 1, 14, 0, 0)
ValueError: day is out of range for month&lt;&#x2F;module&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Instead, use Python&#x27;s timedelta, which is designed for this purpose:&lt;&#x2F;p&gt;
&lt;pre&gt;from datetime import datetime, timedelta

td = datetime.today()
tm2 = td + timedelta(days=1)

print(&quot;tm2=%s&quot; % str(tm2))&lt;&#x2F;pre&gt;
&lt;p&gt;And it&#x27;s easier to read too.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Broken Levels Challenge - Egham Raspberry Pi Jam July 2017</title>
          <pubDate>Sun, 23 Jul 2017 16:42:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/07/23/broken-levels-challenge-egham-raspberry-pi-jam-july-2017/</link>
          <guid>https://artificialworlds.net/blog/2017/07/23/broken-levels-challenge-egham-raspberry-pi-jam-july-2017/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/07/23/broken-levels-challenge-egham-raspberry-pi-jam-july-2017/">&lt;p&gt;Today at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;EghamJam&quot;&gt;Egham Raspberry Pi Jam&lt;&#x2F;a&gt; we did two things:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-the-broken-levels-challenge&quot;&gt;1. The Broken Levels Challenge&lt;&#x2F;h3&gt;
&lt;p&gt;Some nasty person came and broke our levels for our game &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; and we need you to fix them!&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;broken-levels-challenge.jpg&quot; alt=&quot;&quot; width=&quot;1200&quot; height=&quot;900&quot; class=&quot;alignnone size-full wp-image-2279&quot;&gt;
&lt;p&gt;To play this game you will need a PC version of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, our &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;broken-levels.zip&quot;&gt;Broken Levels&lt;&#x2F;a&gt;, and the &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;broken-levels-challenge.pdf&quot;&gt;instruction sheets&lt;&#x2F;a&gt;. Let us know how you get on!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-python-traffic-lights-programming-workshop&quot;&gt;2. Python Traffic Lights Programming Workshop&lt;&#x2F;h3&gt;
&lt;p&gt;I ran a workshop to learn a bit of Python programming using this resource sheet &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;learning&#x2F;jam-worksheets&#x2F;traffic-lights&#x2F;pi-stop-traffic-lights.md&quot;&gt;Pi Stop Traffic Lights&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We had a lot of fun, and hopefully some people even learnt a little bit of coding.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A lot of concentration. Python, GPIOZero. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;4tronix_uk&quot;&gt;@4tronix_uk&lt;&#x2F;a&gt; pi-stop. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;rjam_chat&quot;&gt;@rjam_chat&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Thank you &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;@andybalaam&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;t.co&#x2F;0cWAvSL6M8&quot;&gt;pic.twitter.com&#x2F;0cWAvSL6M8&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— Egham Raspberry Jam (@EghamJam) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;EghamJam&#x2F;status&#x2F;889133371242291200&quot;&gt;July 23, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
      <item>
          <title>Women Who Code workshop on &quot;Write your own programming language&quot;</title>
          <pubDate>Thu, 29 Jun 2017 09:20:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/29/women-who-code-workshop-on-write-your-own-programming-language/</link>
          <guid>https://artificialworlds.net/blog/2017/06/29/women-who-code-workshop-on-write-your-own-programming-language/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/29/women-who-code-workshop-on-write-your-own-programming-language/">&lt;p&gt;On Wednesday 28th June 2017 a group of people from OpenMarket went to the Fora office space in Clerkenwell, London to run a workshop with the Women Who Code group, who work to help women achieve their career goals.&lt;&#x2F;p&gt;
&lt;p&gt;OpenMarket provided the workshop &quot;Write your own programming language&quot; and funded the food, and the venue was provided gratis by Fora.&lt;&#x2F;p&gt;
&lt;p&gt;We started the evening with some networking and food:&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wwc-networking.jpg&quot; alt=&quot;networking&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;alignnone size-full wp-image-2243&quot;&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wwc-food.jpg&quot; alt=&quot;food&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;alignnone size-full wp-image-2241&quot;&gt;
&lt;p&gt;but most of the time was spent coding:&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wwc-coding.jpg&quot; alt=&quot;coding&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;alignnone size-full wp-image-2240&quot;&gt;
&lt;p&gt;with lots of help from our OpenMarket helpers:&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;wwc-helpers.jpg&quot; alt=&quot;helpers&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;alignnone size-full wp-image-2242&quot;&gt;
&lt;p&gt;The feedback we got was very positive:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thanks &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;@andybalaam&lt;&#x2F;a&gt; for that great Write Your Own Programming Language workshop! &amp;amp; thanks &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;openmarket&quot;&gt;@openmarket&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;fora_space&quot;&gt;@fora_space&lt;&#x2F;a&gt; for sponsoring&#x2F;hosting! &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;t.co&#x2F;6U0DClAHMD&quot;&gt;pic.twitter.com&#x2F;6U0DClAHMD&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— Women Who Code LDN (@WWCLondon) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WWCLondon&#x2F;status&#x2F;880169210588139520&quot;&gt;June 28, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;It&#x27;s all about the cool&quot; Thx &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;@andybalaam&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
Great workshop &lt;img src=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WomenWhoCode&quot; alt=&quot;@WomenWhoCode&quot; &#x2F;&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;coderinheels&quot;&gt;@coderinheels&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;t.co&#x2F;El3bAeRtK4&quot;&gt;pic.twitter.com&#x2F;El3bAeRtK4&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— Sandrine Pataut (@SPataut) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;SPataut&#x2F;status&#x2F;880146958370910208&quot;&gt;June 28, 2017&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Everyone seemed to be having fun, so we hope we might get invited back to do more in future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-do-this&quot;&gt;Why do this?&lt;&#x2F;h2&gt;
&lt;p&gt;At OpenMarket we want to improve our diversity, and we have started by looking at gender diversity specifically. By being involved with events like this we hope to learn how we can make our company better at welcoming and supporting employees, encourage people from under-represented groups to apply to work here, and improve the general climate in our industry.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;thank-you&quot;&gt;Thank you&lt;&#x2F;h2&gt;
&lt;p&gt;A huge thank you to the OpenMarket people (from London and Guadalajara!) who helped out - I think people felt welcome and there was plenty of help available for the attendees - you did a great job.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you also for the great response from everyone in our London office - several people in the office wanted to come but couldn&#x27;t make it on the night - I am hoping we will get more opportunities in future.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re also really grateful to OpenMarket for funding the food, to Fora for providing the space, and to Women Who Code for doing such great work to improve our industry.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;links&quot;&gt;Links&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.openmarket.com&#x2F;company&#x2F;careers&#x2F;&quot;&gt;OpenMarket is hiring&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;foraspace.com&#x2F;&quot;&gt;Fora&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;WWCLondon&quot;&gt;Women Who Code LDN&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;write-your-own-language&#x2F;write-your-own-language.html&quot;&gt;Write your own programming language&lt;&#x2F;a&gt; (workshop slides)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;datecalc&quot;&gt;datecalc&lt;&#x2F;a&gt; (my version of the language we wrote)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;[Photos by David Lawson.]&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Running a virtualenv with a custom-built Python</title>
          <pubDate>Tue, 27 Jun 2017 00:31:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/27/running-a-virtualenv-with-a-custom-built-python/</link>
          <guid>https://artificialworlds.net/blog/2017/06/27/running-a-virtualenv-with-a-custom-built-python/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/27/running-a-virtualenv-with-a-custom-built-python/">&lt;p&gt;For my &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;attempt&lt;&#x2F;a&gt; to improve the asyncio.as_completed Python standard library function I needed to build a local copy of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&quot;&gt;cpython&lt;&#x2F;a&gt; (the Python interpreter).&lt;&#x2F;p&gt;
&lt;p&gt;To test it, I needed the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;aiohttp.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;aiohttp&lt;&#x2F;a&gt; module, which is not part of the standard library, so the easiest way to get it was using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;virtualenv.pypa.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;virtualenv&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the recipe I used to get a virtualenv and install packages using pip with a custom-built Python:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ~&#x2F;code&#x2F;public&#x2F;cpython&#x2F;python -m venv env
$ . env&#x2F;bin&#x2F;activate
(env) $ pip install aiohttp
(env) $ python mycode.py&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Adding a concurrency limit to Python&#x27;s asyncio.as_completed</title>
          <pubDate>Tue, 27 Jun 2017 00:16:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/27/adding-a-concurrency-limit-to-pythons-asyncio-as_completed/</link>
          <guid>https://artificialworlds.net/blog/2017/06/27/adding-a-concurrency-limit-to-pythons-asyncio-as_completed/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/27/adding-a-concurrency-limit-to-pythons-asyncio-as_completed/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;basic-ideas-of-python-3-asyncio-concurrency&#x2F;&quot;&gt;asyncio basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;&quot;&gt;large numbers in parallel&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;parallel HTTP requests&lt;&#x2F;a&gt;, adding to stdlib&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; I demonstrated how the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;asyncioplus&#x2F;src&#x2F;branch&#x2F;master&#x2F;asyncioplus&#x2F;limited_as_completed.py&quot;&gt;limited_as_completed&lt;&#x2F;a&gt; method allows us to run a very large number of tasks using concurrency, but limiting the number of concurrent tasks to a sensible limit to ensure we don&#x27;t exhaust resources like memory or operating system file handles.&lt;&#x2F;p&gt;
&lt;p&gt;I think this could be a useful addition to the Python standard library, so I have been working on a modification to the current asyncio.as_completed method. My work so far is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;pull&#x2F;2424&quot;&gt;limited-as_completed&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I ran similar tests to the ones I ran for the last blog post with this code to validate that the modified standard library version achieves the same goals as before.&lt;&#x2F;p&gt;
&lt;p&gt;I used an identical copy of timed from the &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; and updated versions of the other files because I was using a much newer version of aiohttp along with the custom-built python I was running.&lt;&#x2F;p&gt;
&lt;p&gt;server looked like:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3

from aiohttp import web
import asyncio
import random

async def handle(request):
    await asyncio.sleep(random.randint(0, 3))
    return web.Response(text=&quot;Hello, World!&quot;)

app = web.Application()
app.router.add_get(&#x27;&#x2F;{name}&#x27;, handle)

web.run_app(app)&lt;&#x2F;pre&gt;
&lt;p&gt;client-async-sem needed me to add a custom TCPConnector to avoid a new limit on the number of concurrent connections that was added to aiohttp in version 2.0. I also need to move the ClientSession usage inside a coroutine to avoid a warning:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3

from aiohttp import ClientSession, TCPConnector
import asyncio
import sys

limit = 1000

async def fetch(url, session):
    async with session.get(url) as response:
        return await response.read()

async def bound_fetch(sem, url, session):
    # Getter function with semaphore.
    async with sem:
        await fetch(url, session)

async def run(r):
    with ClientSession(connector=TCPConnector(limit=limit)) as session:
        url = &quot;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&quot;
        tasks = []
        # create instance of Semaphore
        sem = asyncio.Semaphore(limit)
        for i in range(r):
            # pass Semaphore and session to every GET request
            task = asyncio.ensure_future(
                bound_fetch(sem, url.format(i), session))
            tasks.append(task)
        responses = asyncio.gather(*tasks)
        await responses

loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.ensure_future(run(int(sys.argv[1]))))&lt;&#x2F;pre&gt;
&lt;p&gt;My new code that uses my proposed extension to as_completed looked like:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3

from aiohttp import ClientSession, TCPConnector
import asyncio
import sys

async def fetch(url, session):
    async with session.get(url) as response:
        return await response.read()

limit = 1000

async def print_when_done():
    with ClientSession(connector=TCPConnector(limit=limit)) as session:
        tasks = (fetch(url.format(i), session) for i in range(r))
        for res in asyncio.as_completed(tasks, limit=limit):
            await res

r = int(sys.argv[1])
url = &quot;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&quot;
loop = asyncio.get_event_loop()
loop.run_until_complete(print_when_done())
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;and with these, we get similar behaviour to the previous post:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-sem 10000
Memory usage: 73640KB	Time: 19.18 seconds
$ .&#x2F;timed .&#x2F;client-async-stdlib 10000
Memory usage: 49332KB	Time: 18.97 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;So the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;pull&#x2F;2424&quot;&gt;implementation&lt;&#x2F;a&gt; I plan to submit to the Python standard library appears to work well. In fact, I think it is better than the one I presented in the previous post, because it uses on_complete callbacks to notice when futures have completed, which reduces the busy-looping we were doing to check for and yield finished tasks.&lt;&#x2F;p&gt;
&lt;p&gt;The Python issue is &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.python.org&#x2F;issue30782&quot;&gt;bpo-30782&lt;&#x2F;a&gt; and the pull request is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;pull&#x2F;2424&quot;&gt;#2424&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note: at first glance, it looks like the aiohttp.ClientSession&#x27;s &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;aiohttp.readthedocs.io&#x2F;en&#x2F;stable&#x2F;migration.html#client-connector&quot;&gt;limit on the number of connections&lt;&#x2F;a&gt; (introduced in version 1.0 and then updated in version 2.0) gives us what we want without any of this extra code, but in fact it only limits the number of connections, not the number of futures we are creating, so it has the same problem of unbounded memory use as the semaphore-based implementation.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alexander Mohr
    &lt;span class=&quot;comment_date&quot;&gt;2017-07-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I created this to solve this problem more generally: &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;thehesiod&#x2F;7081ab165b9a0d4de2e07d321cc2391d&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;thehesiod&#x2F;7081ab165b9...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-07-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Alexander, that is cool.  In what way is it more general?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Reto Kaiser
    &lt;span class=&quot;comment_date&quot;&gt;2017-10-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Alexander’s script has the advantage that it allows to add coroutines after the loop has been started. Can be useful if one async operations should schedule other operations recursively.&lt;br&#x2F;&gt;
Here’s another solution:&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;njam&#x2F;e19a497185a9f657dc77429eed3aea07&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;njam&#x2F;e19a497185a9f657...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;btw I guess for most usecases setting aiohttp’s connection limit should be sufficient:&lt;br&#x2F;&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;aiohttp.readthedocs.io&#x2F;en&#x2F;stable&#x2F;client.html#limiting-connection-pool-size&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;aiohttp.readthedocs.io&#x2F;en&#x2F;stable&#x2F;clien...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-10-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Reto, good points.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andrey Paramonov
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the inspiration!&lt;br&#x2F;&gt;
I’m currently using the following generalization of &lt;code&gt;asyncio.gather&lt;&#x2F;code&gt; for similar problem:&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;k&quot;&gt;async&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;def&lt;&#x2F;span&gt; &lt;span class=&quot;nf&quot;&gt;igather&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;kc&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    &lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    &lt;span class=&quot;k&quot;&gt;while&lt;&#x2F;span&gt; &lt;span class=&quot;kc&quot;&gt;True&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        &lt;span class=&quot;k&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;&#x2F;span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;islice&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;&#x2F;span&gt; &lt;span class=&quot;kc&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            &lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;task&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        &lt;span class=&quot;k&quot;&gt;if&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            &lt;span class=&quot;n&quot;&gt;done&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;await&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;wait&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;pending&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;return_when&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;asyncio&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;FIRST_COMPLETED&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            &lt;span class=&quot;k&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;&#x2F;span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;done&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                &lt;span class=&quot;k&quot;&gt;yield&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;task&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        &lt;span class=&quot;k&quot;&gt;else&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            &lt;span class=&quot;k&quot;&gt;break&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andrey Paramonov
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the inspiration!&lt;&#x2F;p&gt;
&lt;p&gt;I’m currently using the following generalization of &lt;code&gt;asyncio.gather&lt;&#x2F;code&gt; for similar problem:&lt;&#x2F;p&gt;
&lt;p&gt;async def igather(tasks, limit=None):&lt;br&#x2F;&gt;
pending = set()&lt;br&#x2F;&gt;
while True:&lt;br&#x2F;&gt;
for task in islice(tasks, limit – len(pending) if limit else None):&lt;br&#x2F;&gt;
pending.add(task)&lt;br&#x2F;&gt;
if pending:&lt;br&#x2F;&gt;
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)&lt;br&#x2F;&gt;
for task in done:&lt;br&#x2F;&gt;
yield task&lt;br&#x2F;&gt;
else:&lt;br&#x2F;&gt;
break&lt;&#x2F;p&gt;
&lt;hr&#x2F;&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Nice, thanks Andrey!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sergey
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Maybe the &quot;meant to be&quot; way to limit number of workers is using queues:&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;asyncio-queue.html#asyncio-queues&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;asyncio-que...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Sergey – yes, maybe we could write some kind of limited-size task pool that pull from a queue.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Raaga J
    &lt;span class=&quot;comment_date&quot;&gt;2020-06-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;What is the difference between a semaphore limit and TCP connector limit ?Can u please explain in detail with an example. I am a beginner in Asyncio. thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>&quot;git what&quot; is &quot;git status&quot; on steroids</title>
          <pubDate>Fri, 23 Jun 2017 01:40:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/23/git-what-is-git-status-on-steroids/</link>
          <guid>https://artificialworlds.net/blog/2017/06/23/git-what-is-git-status-on-steroids/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/23/git-what-is-git-status-on-steroids/">&lt;p&gt;For when git status is not enough, I wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;git-what&quot;&gt;git what&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;screenshot.png&quot; alt=&quot;&quot; width=&quot;618&quot; height=&quot;322&quot; class=&quot;alignnone size-full wp-image-2205&quot;&gt;
&lt;p&gt;If you often have a few branches on the go, it could be useful.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Making 100 million requests with Python aiohttp</title>
          <pubDate>Mon, 12 Jun 2017 09:41:51 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/12/making-100-million-requests-with-python-aiohttp/</link>
          <guid>https://artificialworlds.net/blog/2017/06/12/making-100-million-requests-with-python-aiohttp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/12/making-100-million-requests-with-python-aiohttp/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;basic-ideas-of-python-3-asyncio-concurrency&#x2F;&quot;&gt;asyncio basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;&quot;&gt;large numbers in parallel&lt;&#x2F;a&gt;, parallel HTTP requests, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;adding to stdlib&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: slides of a talk I gave at the London Python Meetup on this: &lt;a href=&quot;&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;python-async&#x2F;python-async.html&quot;&gt;Talk slides: Making 100 million HTTP requests with Python aiohttp&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: see how Cristian Garcia improved on this code here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@cgarciae&#x2F;making-an-infinite-number-of-requests-with-python-aiohttp-pypeln-3a552b97dc95&quot;&gt;Making an Unlimited Number of Requests with Python aiohttp + pypeln&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been working on how to make a very large number of HTTP requests using Python&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;asyncio.html&quot;&gt;asyncio&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;aiohttp.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;aiohttp&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;PaweÅ‚ Miech&#x27;s post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pawelmhm.github.io&#x2F;asyncio&#x2F;python&#x2F;aiohttp&#x2F;2016&#x2F;04&#x2F;22&#x2F;asyncio-aiohttp.html&quot;&gt;Making 1 million requests with python-aiohttp&lt;&#x2F;a&gt; taught me how to think about this, and got us a long way, with 1 million requests running in a reasonable time, but I need to go further.&lt;&#x2F;p&gt;
&lt;p&gt;PaweÅ‚&#x27;s approach limits the number of requests that are in progress, but it uses an unbounded amount of memory to hold the futures that it wants to execute.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;See also&lt;&#x2F;strong&gt;: 2 excellent related posts by Quentin Pradet: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;quentin.pradet.me&#x2F;blog&#x2F;how-do-you-rate-limit-calls-with-asyncio.html&quot;&gt;How do you rate limit calls with asyncio?&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;quentin.pradet.me&#x2F;blog&#x2F;how-do-you-limit-memory-usage-with-asyncio.html&quot;&gt;How do you limit memory usage with asyncio?&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We can avoid using unbounded memory by using the limited_as_completed function I outined in my &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;%22&quot;&gt;previous post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;server&quot;&gt;Server&lt;&#x2F;h4&gt;
&lt;p&gt;We have a server program &quot;server&quot;:&lt;&#x2F;p&gt;
&lt;p&gt;(Note it differs from PaweÅ‚&#x27;s version because I am using an older version of aiohttp which has fewer convenient features.)&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3.5

from aiohttp import web
import asyncio
import random

async def handle(request):
    await asyncio.sleep(random.randint(0, 3))
    return web.Response(text=&quot;Hello, World!&quot;)

async def init():
    app = web.Application()
    app.router.add_route(&#x27;GET&#x27;, &#x27;&#x2F;{name}&#x27;, handle)
    return await loop.create_server(
        app.make_handler(), &#x27;127.0.0.1&#x27;, 8080)

loop = asyncio.get_event_loop()
loop.run_until_complete(init())
loop.run_forever()&lt;&#x2F;pre&gt;
&lt;p&gt;This just responds &quot;Hello, World!&quot; to every request it receives, but after an artificial delay of 0-3 seconds.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;synchronous-client&quot;&gt;Synchronous client&lt;&#x2F;h4&gt;
&lt;p&gt;As a baseline, we have a synchronous client &quot;client-sync&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3.5

import requests
import sys

url = &quot;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&quot;
for i in range(int(sys.argv[1])):
    requests.get(url.format(i)).text&lt;&#x2F;pre&gt;
&lt;p&gt;This waits for each request to complete before making the next one. Like the other clients below, it takes the number of requests to make as a command-line argument.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;async-client-using-semaphores&quot;&gt;Async client using semaphores&lt;&#x2F;h4&gt;
&lt;p&gt;Copied mostly verbatim from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pawelmhm.github.io&#x2F;asyncio&#x2F;python&#x2F;aiohttp&#x2F;2016&#x2F;04&#x2F;22&#x2F;asyncio-aiohttp.html&quot;&gt;Making 1 million requests with python-aiohttp&lt;&#x2F;a&gt; we have an async client &quot;client-async-sem&quot; that uses a semaphore to restrict the number of requests that are in progress at any time to 1000:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3.5

from aiohttp import ClientSession
import asyncio
import sys

limit = 1000

async def fetch(url, session):
    async with session.get(url) as response:
        return await response.read()

async def bound_fetch(sem, url, session):
    # Getter function with semaphore.
    async with sem:
        await fetch(url, session)

async def run(session, r):
    url = &quot;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&quot;
    tasks = []
    # create instance of Semaphore
    sem = asyncio.Semaphore(limit)
    for i in range(r):
        # pass Semaphore and session to every GET request
        task = asyncio.ensure_future(bound_fetch(sem, url.format(i), session))
        tasks.append(task)
    responses = asyncio.gather(*tasks)
    await responses

loop = asyncio.get_event_loop()
with ClientSession() as session:
    loop.run_until_complete(asyncio.ensure_future(run(session, int(sys.argv[1]))))&lt;&#x2F;pre&gt;
&lt;h4 id=&quot;async-client-using-limited-as-completed&quot;&gt;Async client using limited_as_completed&lt;&#x2F;h4&gt;
&lt;p&gt;The new client I am presenting here uses limited_as_completed from the &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;%22&quot;&gt;previous post&lt;&#x2F;a&gt;. This means it can make a generator that provides the futures to wait for as they are needed, instead of making them all at the beginning.&lt;&#x2F;p&gt;
&lt;p&gt;It is called &quot;client-async-as-completed&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python3.5

from aiohttp import ClientSession
import asyncio
from itertools import islice
import sys

def limited_as_completed(coros, limit):
    futures = [
        asyncio.ensure_future(c)
        for c in islice(coros, 0, limit)
    ]
    async def first_to_finish():
        while True:
            await asyncio.sleep(0)
            for f in futures:
                if f.done():
                    futures.remove(f)
                    try:
                        newf = next(coros)
                        futures.append(
                            asyncio.ensure_future(newf))
                    except StopIteration as e:
                        pass
                    return f.result()
    while len(futures) &amp;gt; 0:
        yield first_to_finish()

async def fetch(url, session):
    async with session.get(url) as response:
        return await response.read()

limit = 1000

async def print_when_done(tasks):
    for res in limited_as_completed(tasks, limit):
        await res

r = int(sys.argv[1])
url = &quot;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&quot;
loop = asyncio.get_event_loop()
with ClientSession() as session:
    coros = (fetch(url.format(i), session) for i in range(r))
    loop.run_until_complete(print_when_done(coros))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;Again, this limits the number of requests to 1000.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;test-setup&quot;&gt;Test setup&lt;&#x2F;h4&gt;
&lt;p&gt;Finally, we have a test runner script called &quot;timed&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env bash

.&#x2F;server &amp;amp;
sleep 1 # Wait for server to start

&#x2F;usr&#x2F;bin&#x2F;time --format &quot;Memory usage: %MKB\tTime: %e seconds&quot; &quot;$@&quot;

# %e Elapsed real (wall clock) time used by the process, in seconds.
# %M Maximum resident set size of the process in Kilobytes.

kill %1&lt;&#x2F;pre&gt;
&lt;p&gt;This runs each process, ensuring the server is restarted each time it runs, and prints out how long it took to run, and how much memory it used.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;results&quot;&gt;Results&lt;&#x2F;h3&gt;
&lt;p&gt;When making only 10 requests, the async clients worked faster because they launched all the requests simultaneously and only had to wait for the longest one (3 seconds). The memory usage of all three clients was fine:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-sync 10
Memory usage: 20548KB	Time: 15.16 seconds
$ .&#x2F;timed .&#x2F;client-async-sem 10
Memory usage: 24996KB	Time: 3.13 seconds
$ .&#x2F;timed .&#x2F;client-async-as-completed 10
Memory usage: 23176KB	Time: 3.13 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;When making 100 requests, the synchronous client was very slow, but all three clients worked eventually:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-sync 100
Memory usage: 20528KB	Time: 156.63 seconds
$ .&#x2F;timed .&#x2F;client-async-sem 100
Memory usage: 24980KB	Time: 3.21 seconds
$ .&#x2F;timed .&#x2F;client-async-as-completed 100
Memory usage: 24904KB	Time: 3.21 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;At this point let&#x27;s agree that life is too short to wait for the synchronous client.&lt;&#x2F;p&gt;
&lt;p&gt;When making 10000 requests, both async clients worked quite quickly, and both had increased memory usage, but the semaphore-based one used almost twice as much memory as the limited_as_completed version:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-sem 10000
Memory usage: 77912KB	Time: 18.10 seconds
$ .&#x2F;timed .&#x2F;client-async-as-completed 10000
Memory usage: 46780KB	Time: 17.86 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;For 1 million requests, the semaphore-based client took 25 minutes on my (32GB RAM) machine. It only used about 10% of my CPU, and it used a lot of memory (over 3GB):&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-sem 1000000
Memory usage: 3815076KB	Time: 1544.04 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;Note: PaweÅ‚&#x27;s version only took 9 minutes on his laptop and used all his CPU, so I wonder whether I have made a mistake somewhere, or whether my version of Python (3.5.2) is not as good as a later one.&lt;&#x2F;p&gt;
&lt;p&gt;The limited_as_completed version ran in a similar amount of time but used 100% of my CPU, and used a much smaller amount of memory (162MB):&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-as-completed 1000000
Memory usage: 162168KB	Time: 1505.75 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;Now let&#x27;s try 100 million requests. The semaphore-based version lasted 10 hours before it was killed by Linux&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kernel.org&#x2F;doc&#x2F;gorman&#x2F;html&#x2F;understand&#x2F;understand016.html&quot;&gt;OOM Killer&lt;&#x2F;a&gt;, but it didn&#x27;t manage to make any requests in this time, because it creates all its futures before it starts making requests:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-sem 100000000
Command terminated by signal 9&lt;&#x2F;pre&gt;
&lt;p&gt;I left the limited_as_completed version over the weekend and it managed to succeed eventually:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;timed .&#x2F;client-async-as-completed 100000000
Memory usage: 294304KB	Time: 150213.15 seconds&lt;&#x2F;pre&gt;
&lt;p&gt;So its memory usage was still very bounded, and it managed to do about 665 requests&#x2F;second over an extended period, which is almost identical to the throughput of the previous cases.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;Making a million requests is usually enough, but when we really need to do a lot of work while keeping our memory usage bounded, it looks like an approach like limited_as_completed is a good way to go. I also think it&#x27;s slightly easier to understand.&lt;&#x2F;p&gt;
&lt;p&gt;In the &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;next post&lt;&#x2F;a&gt; I describe my attempt to get something like this added to the Python standard library.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jack Brown
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I am using this to scrape data from a website however the website will place a temp block on IP addresses if it detects excessive use. Is there a way I can place a delay between requests? I am assuming I can use the time.sleep(seconds) function but unsure of the best place to insert within script.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Jack, I think you could put a sleep inside the &quot;async with&quot; block in fetch(), but I’d seriously suggest you avoid spamming a web site against its terms and conditions!  Maybe contact the site about getting hold of their data another way?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nam Vu
    &lt;span class=&quot;comment_date&quot;&gt;2017-10-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;How can we share cookies for all of 100 M requests?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-10-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Nam, since many of the requests are happening in parallel, sharing cookies may not work the way you need, but apart from that caveat, you should be able to provide cookies the way you normally do it using the requests library.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ruslan
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;That is a nice experiment. Andy, the fact that your client-async-as-completed uses 100% CPU is not a good thing. It indicates a problem with using busy-waiting. Your &quot;while True&quot; loop continuously checks for task completion, and that causes 100% CPU utilization. A better approach is to use&lt;&#x2F;p&gt;
&lt;p&gt;while futures:&lt;br&#x2F;&gt;
done, futures = await asyncio.wait(futures, return_when=asyncio.FIRST_COMPLETED)&lt;br&#x2F;&gt;
# then re-fill the set of futures from the coros iterable&lt;&#x2F;p&gt;
&lt;p&gt;asyncio.wait does not do busy-waiting&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Awesome, thanks ruslan!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Shih-Wen Su
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-31&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is a great post! I learned a lot from it.&lt;br&#x2F;&gt;
I’m also thinking about how to use aiohttp for a similar task.&lt;br&#x2F;&gt;
Inspired by this post, I’m using only the semaphore to control the concurrent requests without exhausting CPU or memory. The only thing I do differently is to acquire the semaphore before creating the future task and release the semaphore after fetching the result.&lt;br&#x2F;&gt;
Code:&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;flyakite&#x2F;100-million-requests-aiohttp&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;flyakite&#x2F;100-million-reque...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Shih-Wen Su – looks great!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi!&lt;br&#x2F;&gt;
I created this library called &lt;code&gt;pypeln&lt;&#x2F;code&gt; – &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&lt;&#x2F;a&gt; for creating many kinds of concurrent data pipelines. It currently supports Processes, Threads and asyncio Tasks.&lt;&#x2F;p&gt;
&lt;p&gt;Your post was an inspiration when implementing the io module!&lt;&#x2F;p&gt;
&lt;p&gt;With pypeln you can easily solve the problem you showed like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;from aiohttp import ClientSession
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;from pypeln import io
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;import sys
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;r = int(sys.argv[1])
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;url = &amp;#34;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&amp;#34;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;with ClientSession() as session:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    data = range(r)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    io.each(lambda i: fetch(url.format(i), session), data, workers=1000)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thanks for sharing you knowledge!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wow Cristian that looks brilliant!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I was planning to write a follow-up using pypeln, but I got this:&lt;&#x2F;p&gt;
&lt;pre&gt; cat client-pypeln
#!&#x2F;usr&#x2F;bin&#x2F;env python3.6
from aiohttp import ClientSession
from pypeln import io
import sys
r = int(sys.argv[1])
url = &amp;#34;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&amp;#34;
with ClientSession() as session:
data = range(r)
io.each(lambda i: fetch(url.format(i), session), data, workers=1000)
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ .&#x2F;client-pypeln 10
.&#x2F;client-pypeln:10: UserWarning: Creating a client session outside of coroutine is a very dangerous idea
with ClientSession() as session:
Creating a client session outside of coroutine
client_session:
Traceback (most recent call last):
File &amp;#34;.&#x2F;client-pypeln&amp;#34;, line 10, in
with ClientSession() as session:
File &amp;#34;&#x2F;usr&#x2F;lib&#x2F;python3&#x2F;dist-packages&#x2F;aiohttp&#x2F;client.py&amp;#34;, line 742, in __enter__
raise TypeError(&amp;#34;Use async with instead&amp;#34;)
TypeError: Use async with instead
Unclosed client session
client_session:
&lt;&#x2F;pre&gt;
&lt;p&gt;Can you give a more complete example?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey Andy,&lt;&#x2F;p&gt;
&lt;p&gt;Glad you liked it!&lt;&#x2F;p&gt;
&lt;p&gt;The error is due to changes in the iohttp library, it seems that ClientSession now has to be run with &quot;async with&quot; instead of &quot;with&quot;. Your original code should no longer work.&lt;&#x2F;p&gt;
&lt;p&gt;Here is a full working example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;from aiohttp import ClientSession
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;from pypeln import io
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;import asyncio
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;import sys
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;async def fetch(url, session):
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    async with session.get(url) as response:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        return await response.read()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;async def main():
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    r = 10
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    url = &amp;#34;http:&#x2F;&#x2F;google.com&amp;#34;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    # r = int(sys.argv[1])
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    # url = &amp;#34;http:&#x2F;&#x2F;localhost:8080&#x2F;{}&amp;#34;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    async with ClientSession() as session:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        data = range(r)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        await io.each(lambda i: fetch(url, session), data, workers=1000, run = False)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;loop = asyncio.get_event_loop()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;loop.run_until_complete(main())
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;print(&amp;#34;Finish&amp;#34;)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;BTW: how do you make a code block on the comments?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Here is a gist for the code: &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;cgarciae&#x2F;4d35bb9f79c6e72533f26abbaeb17fb2&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;cgarciae&#x2F;4d35bb9f79c6...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You can add &quot;pre&quot; tag around your code, I think.  (I can, anyway.)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, I will test it out and hopefully write a new post.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hmm, something is not working the way we expect – it took longer for this code to do 1000 requests than the client-async-as-completed took to do 10000:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;&#x2F;p&gt;&lt;pre&gt;&lt;br&#x2F;&gt;
$ .&#x2F;timed .&#x2F;client-pypeln 1000&lt;br&#x2F;&gt;
Memory usage: 38672KB	Time: 21.66 seconds&lt;br&#x2F;&gt;
&lt;&#x2F;pre&gt;&lt;p&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you very much for the feedback!&lt;br&#x2F;&gt;
If possible, can we continue the discussion in this issue: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&#x2F;issues&#x2F;9&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&#x2F;issues&#x2F;9&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I looked at the code and made some optimizations, please update to the development code like this before you try:&lt;&#x2F;p&gt;
&lt;p&gt;pip install git+&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln@develop&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln@develop&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cristian Garcia
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Maybe include the -U flag:&lt;&#x2F;p&gt;
&lt;p&gt;pip install -U git+&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln@develop&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln@develop&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Continuing here: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&#x2F;issues&#x2F;9&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;cgarciae&#x2F;pypeln&#x2F;issues&#x2F;9&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;See &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@cgarciae&#x2F;making-an-infinite-number-of-requests-with-python-aiohttp-pypeln-3a552b97dc95&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;medium.com&#x2F;@cgarciae&#x2F;making-an-infini...&lt;&#x2F;a&gt; for Christian’s new improved version.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Increase amounts of requests per second – PythonCharm
    &lt;span class=&quot;comment_date&quot;&gt;2018-11-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] and aiohttp might be even more noteworthy, but, I guess, it would be easier to learn a version of an already […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Vishwa
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi ,&lt;&#x2F;p&gt;
&lt;p&gt;This is great analysis on timecomplexity.&lt;br&#x2F;&gt;
Looking for similar requirement with Python based Unit test framework:&lt;br&#x2F;&gt;
1. Fire 100000 unique URL requests in parallel ( Not sequential and not repetitive requests)  — Thread1&lt;br&#x2F;&gt;
2. Verify HTTP response status code ( eg: 200, 403, 404 etc)  of each requests in parallel  , may be handled via different thread — Thread2&lt;br&#x2F;&gt;
3. Verify Response headers of each URL requests  —&amp;gt; may be handled via different thread – Thread3&lt;br&#x2F;&gt;
4. Login to remote machine ( to which requests is fired) and verify logs  —&amp;gt; Can this be done using RpyC  — Thread4&lt;&#x2F;p&gt;
&lt;p&gt;any unit test framework  like tornado or different framework using asyncio&#x2F;aiohttp suitable with above requirements in optimized time fashion.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    marius-joe
    &lt;span class=&quot;comment_date&quot;&gt;2020-08-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy and Cristian,&lt;br&#x2F;&gt;
great development you did here, thank you.&lt;&#x2F;p&gt;
&lt;p&gt;I just have one question:&lt;br&#x2F;&gt;
Has Ruslan’s suggestion to lower the CPU consumption been also imported to the pypeln library ?&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;#comment-185334&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-08-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, I don’t know I’m afraid.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Arthur
    &lt;span class=&quot;comment_date&quot;&gt;2020-09-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;Nice post – I would suggest looking into Queues to manage more easily the memory consumption:&lt;br&#x2F;&gt;
– you can insert 100M jobs into an asyncio queue: &lt;code&gt;queue = asyncio.Queue(); [queue.put_nowait(i) for i in range(r)]&lt;&#x2F;code&gt;. This will only consume a few 100s of MB of RAM.&lt;br&#x2F;&gt;
– then you can create a worker task that gets a job from the queue, run it, and moves on the the next one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;async def worker(queue):
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    while True:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        job = await queue.get()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        await actual_work(job) # you might want to wrap this in a try&#x2F;except to handle crappy jobs
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        job.task_done()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;ul&gt;
&lt;li&gt;You can launch as many concurrent tasks as necessary with asyncio.create_task&lt;br&#x2F;&gt;
&lt;code&gt;workers = [asyncio.create_task(worker(queue)) for _ in range(1000)]&lt;&#x2F;code&gt;&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;And wait for everything to be finished before cancelling the workers:&lt;br&#x2F;&gt;
&lt;code&gt;
await queue.join()
for task in workers:
task.cancel()
with contextlib.suppress(asyncio.CancelledError):
await task
&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Lukasz
    &lt;span class=&quot;comment_date&quot;&gt;2021-01-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;thanks for a great article! Aiohttp got my intrest, as I’m trying to gather some basic info (not the whole response, but things like status, redirect history, etc.), and async approach seems to be the best. I had some memory issues as I’m trying to run my script on over a milion urls, and found your solution extremely helpful.&lt;&#x2F;p&gt;
&lt;p&gt;I’m wondering however, have you ever tested it outside of the setup described in the article (i.e. with real urls, some of which might be broken or temporarily unavailable)? I guess I’m sort of reproducing your approach and adjusting it to my needs, however I’ve noticed that performance of my script dminishes greatly over time. For example, I was able to check 20K urls in roughly 20 mins, but then while running the script on a longer list, I got to 125K after 9 hours. Moreover, adjusting the task limit doesn’t seem to influence much – when I run it on 20K urls it was 20 mins regardless of whether I set the limit to 100 or 1000.&lt;&#x2F;p&gt;
&lt;p&gt;Would you be able to help me with that? I’m not an expert on general programming (I use python primarily for analytical stuff), so I could have missed something. Do you know what this performance degradation might be caused by?&lt;&#x2F;p&gt;
&lt;p&gt;Please see the code I’m using below (I’ve also tried to mix the ‘as completed’ with semaphores, but eventually, I think it doesn’t make too much sense).&lt;&#x2F;p&gt;
&lt;p&gt;import pandas as pd&lt;br&#x2F;&gt;
import asyncio, aiohttp&lt;br&#x2F;&gt;
import time&lt;br&#x2F;&gt;
from itertools import islice&lt;&#x2F;p&gt;
&lt;p&gt;def task_limiter(tasks, task_limit):&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;futures = [asyncio.ensure_future(c) for c in islice(tasks, 0, task_limit)]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;async def task_list_manager():
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    while True:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        await asyncio.sleep(0)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        for f in futures:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            if f.done():
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                futures.remove(f)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                try:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                    newf = next(tasks)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                    futures.append(asyncio.ensure_future(newf))
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                except StopIteration:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                    pass
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                return f.result()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;while len(futures) &amp;gt; 0:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    yield task_list_manager()
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;async def collect_url_info(session, url, n):&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;try:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    async with await session.get(url) as response:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        redirect_history = []
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        for i in range(len(response.history)):
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            if str(url) == str(response.history[i].url):
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                continue
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            else:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                redirect_history.append(str(response.history[i].url))
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        print(f&amp;#39;Task {n} done&amp;#39;)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        return response.status, response.reason, redirect_history
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;except Exception as e:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    msg = str(type(e)) + &amp;#34;|||&amp;#34; + str(e)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    print(f&amp;#39;Task {n} done with status -1 {msg}&amp;#39;)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    return (-1, msg, list())
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;async def bound_collect(sem, session, url, n):&lt;br&#x2F;&gt;
async with sem:&lt;br&#x2F;&gt;
return await collect_url_info(session, url, n)&lt;&#x2F;p&gt;
&lt;p&gt;async def run_url_check(urls, task_limit, conn_limit, timeout):&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;n&quot;&gt;sem&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;asyncio&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;Semaphore&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;conn_limit&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;n&quot;&gt;connector&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;aiohttp&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;TCPConnector&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;limit&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Accept&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;text&#x2F;html,application&#x2F;xhtml+xml,application&#x2F;xml;q=0.9,image&#x2F;webp,image&#x2F;apng,*&#x2F;*;q=0.8,application&#x2F;signed-exchange;v=b3;q=0.9&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Accept-Encoding&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;gzip, deflate&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Accept-Language&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;en-GB,en-US;q=0.9,en;q=0.8&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Dnt&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Upgrade-Insecure-Requests&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;User-Agent&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Mozilla&#x2F;5.0 (Windows NT 10.0; Win64; x64) AppleWebKit&#x2F;537.36 (KHTML, like Gecko) Chrome&#x2F;74.0.3729.169 Safari&#x2F;537.36&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;Referer&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;sa&quot;&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;google.com&lt;&#x2F;span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;#34;&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;p&quot;&gt;}&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;n&quot;&gt;async&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;with&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;aiohttp&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;ClientSession&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;connector&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;connector&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;timeout&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;as&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    &lt;span class=&quot;n&quot;&gt;tasks&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;collect_url_info&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;urls&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    &lt;span class=&quot;n&quot;&gt;result&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;&#x2F;span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;len&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;urls&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    &lt;span class=&quot;k&quot;&gt;for&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;&#x2F;span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;enumerate&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;task_limiter&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;tasks&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;task_limit&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        &lt;span class=&quot;n&quot;&gt;result&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;&#x2F;span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;&#x2F;span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;await&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;&lt;span class=&quot;k&quot;&gt;return&lt;&#x2F;span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;def prep_output_df(df, url_col, url_info):&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;df[&amp;#39;status_code&amp;#39;] = [x[0] for x in url_info]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;df[&amp;#39;status_reason&amp;#39;] = [x[1] for x in url_info]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;df[&amp;#39;redirect_history&amp;#39;] = [str(x[2]) for x in url_info]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;df[&amp;#39;final_url&amp;#39;] = [x[2][-1] if len(x[2]) != 0 else &amp;#39;&amp;#39; for x in url_info]
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;df[&amp;#39;redirect_flag&amp;#39;] = df[&amp;#39;final_url&amp;#39;].apply(lambda x: 1 if x != &amp;#39;&amp;#39; else 0, 1)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;return df
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;###SCRIPT&lt;br&#x2F;&gt;
start = time.time()&lt;&#x2F;p&gt;
&lt;p&gt;task_limit = 10000&lt;br&#x2F;&gt;
#conn_limit = 1000&lt;br&#x2F;&gt;
timeout = aiohttp.ClientTimeout(total=None, sock_connect=60, sock_read=60)&lt;&#x2F;p&gt;
&lt;p&gt;input_path = ‘input.csv’&lt;br&#x2F;&gt;
data = pd.read_csv(input_path, nrows = 20000)&lt;br&#x2F;&gt;
url_col = ‘WEBSITE’&lt;br&#x2F;&gt;
urls = data[url_col]&lt;&#x2F;p&gt;
&lt;p&gt;loop = asyncio.get_event_loop()&lt;br&#x2F;&gt;
url_info = loop.run_until_complete(run_url_check(urls, task_limit, conn_limit, timeout))&lt;&#x2F;p&gt;
&lt;p&gt;data = prep_output_df(data, url_col, url_info)&lt;&#x2F;p&gt;
&lt;p&gt;total_requests = len(data)&lt;br&#x2F;&gt;
error_requests = (data[‘status_code’] == -1).sum()&lt;br&#x2F;&gt;
print(‘NUMBER OF REQUESTS:’)&lt;br&#x2F;&gt;
print(total_requests)&lt;br&#x2F;&gt;
print(‘NUMBER OF ERRORS:’)&lt;br&#x2F;&gt;
print(error_requests)&lt;br&#x2F;&gt;
print(‘ERROR RATE:’)&lt;br&#x2F;&gt;
print((error_requests&#x2F;total_requests))&lt;&#x2F;p&gt;
&lt;p&gt;output_path = ‘out.csv’&lt;br&#x2F;&gt;
data.to_csv(output_path, index=False)&lt;&#x2F;p&gt;
&lt;p&gt;end = time.time()&lt;&#x2F;p&gt;
&lt;p&gt;print(f’Time taken: {end-start}s’)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2021-01-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Lukasz, my first guess would be that the site(s) you are connecting to may slow down or throttle you if you hit them this hard.  It might be worth logging the time between making a request and receiving a response.  Another guess: if you’re storing the responses in memory, you may be starting to use up your computer’s memory space.  If memory gets tight, the machine will slow right down.  Does it seem unresponsive?  The solution in that case would be to write responses to a file as you go, instead of doing it at the end.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Tibabalase O
    &lt;span class=&quot;comment_date&quot;&gt;2021-09-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey Andy, thanks for the detailed post! Sorry to ask if you could go into more detail, but I’m having trouble understanding the &lt;code&gt;limited_as_completed&lt;&#x2F;code&gt; fx, are there any plans to maybe go a bit deeper eventually?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2021-09-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;No plans to go deeper I’m afraid!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Hacer solicitudes asincrónicas más rápido - python en Español
    &lt;span class=&quot;comment_date&quot;&gt;2022-09-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] #1: Realicé una prueba comparativa similar a una en este sitio y 5.000 solicitudes completadas en 3,5 segundos. Así que parece que el hardware no es el problema […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Python - printing UTC dates in ISO8601 format with time zone</title>
          <pubDate>Fri, 09 Jun 2017 09:06:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/06/09/python-printing-utc-dates-in-iso8601-format-with-time-zone/</link>
          <guid>https://artificialworlds.net/blog/2017/06/09/python-printing-utc-dates-in-iso8601-format-with-time-zone/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/06/09/python-printing-utc-dates-in-iso8601-format-with-time-zone/">&lt;p&gt;By default, when you make a UTC date from a Unix timestamp in Python and print it in ISO format, it has no time zone:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3
&amp;gt;&amp;gt;&amp;gt; from datetime import datetime
&amp;gt;&amp;gt;&amp;gt; datetime.utcfromtimestamp(1496998804).isoformat()
&#x27;2017-06-09T09:00:04&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;Whenever you talk about a datetime, I think you should always include a time zone, so I find this problematic.&lt;&#x2F;p&gt;
&lt;p&gt;The solution is to mention the timezone explicitly when you create the datetime:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3
&amp;gt;&amp;gt;&amp;gt; from datetime import datetime, timezone
&amp;gt;&amp;gt;&amp;gt; datetime.fromtimestamp(1496998804, tz=timezone.utc).isoformat()
&#x27;2017-06-09T09:00:04+00:00&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;Note, including the timezone explicitly works the same way when creating a datetime in other ways:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3
&amp;gt;&amp;gt;&amp;gt; from datetime import datetime, timezone
&amp;gt;&amp;gt;&amp;gt; datetime(2017, 6, 9).isoformat()
&#x27;2017-06-09T00:00:00&#x27;
&amp;gt;&amp;gt;&amp;gt; datetime(2017, 6, 9, tzinfo=timezone.utc).isoformat()
&#x27;2017-06-09T00:00:00+00:00&#x27;&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Derek
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Is there an easy way to use ‘Z’ instead of the more clumsy ‘+00:00’ at the end of UTC time?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Python 3 - large numbers of tasks with limited concurrency</title>
          <pubDate>Wed, 31 May 2017 02:09:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/31/python-3-large-numbers-of-tasks-with-limited-concurrency/</link>
          <guid>https://artificialworlds.net/blog/2017/05/31/python-3-large-numbers-of-tasks-with-limited-concurrency/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/31/python-3-large-numbers-of-tasks-with-limited-concurrency/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;basic-ideas-of-python-3-asyncio-concurrency&#x2F;&quot;&gt;asyncio basics&lt;&#x2F;a&gt;, large numbers in parallel, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;parallel HTTP requests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;adding to stdlib&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I am interested in running large numbers of tasks in parallel, so I need something like asyncio.as_completed, but taking an iterable instead of a list, and with a limited number of tasks running concurrently. First, let&#x27;s try to build something pretty much equivalent to asyncio.as_completed. Here is my attempt, but I&#x27;d welcome feedback from readers who know better:&lt;&#x2F;p&gt;
&lt;pre&gt;# Note this is not a coroutine - it returns
# an iterator - but it crucially depends on
# work being done inside the coroutines it
# yields - those coroutines empty out the
# list of futures it holds, and it will not
# end until that list is empty.
def my_as_completed(coros):

    # Start all the tasks
    futures = [asyncio.ensure_future(c) for c in coros]

    # A coroutine that waits for one of the
    # futures to finish and then returns
    # its result.
    async def first_to_finish():

        # Wait forever - we could add a
        # timeout here instead.
        while True:

            # Give up control to the scheduler
            # - otherwise we will spin here
            # forever!
            await asyncio.sleep(0)

            # Return anything that has finished
            for f in futures:
                if f.done():
                    futures.remove(f)
                    return f.result()

    # Keep yielding a waiting coroutine
    # until all the futures have finished.
    while len(futures) &amp;gt; 0:
        yield first_to_finish()&lt;&#x2F;pre&gt;
&lt;p&gt;The above can be substituted for asyncio.as_completed in the code that uses it in &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;basic-ideas-of-python-3-asyncio-concurrency&#x2F;&quot;&gt;the first article&lt;&#x2F;a&gt;, and it seems to work. It also makes a reasonable amount of sense to me, so it may be correct, but I&#x27;d welcome comments and corrections.&lt;&#x2F;p&gt;
&lt;p&gt;my_as_completed above accepts an iterable and returns a generator producing results, but inside it starts all tasks concurrently, and stores all the futures in a list. To handle bigger lists we will need to do better, by limiting the number of running tasks to a sensible number.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start with a test program:&lt;&#x2F;p&gt;
&lt;pre&gt;import asyncio
async def mycoro(number):
    print(&quot;Starting %d&quot; % number)
    await asyncio.sleep(1.0 &#x2F; number)
    print(&quot;Finishing %d&quot; % number)
    return str(number)

async def print_when_done(tasks):
    for res in asyncio.as_completed(tasks):
        print(&quot;Result %s&quot; % await res)

coros = [mycoro(i) for i in range(1, 101)]

loop = asyncio.get_event_loop()
loop.run_until_complete(print_when_done(coros))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;This uses asyncio.as_completed to run 100 tasks and, because I adjusted the asyncio.sleep command to wait longer for earlier tasks, it prints something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ time python3 python-async.py
Starting 47
Starting 93
Starting 48
...
Finishing 93
Finishing 94
Finishing 95
...
Result 93
Result 94
Result 95
...
Finishing 46
Finishing 45
Finishing 42
...
Finishing 2
Result 2
Finishing 1
Result 1

real    0m1.590s
user    0m0.600s
sys 0m0.072s&lt;&#x2F;pre&gt;
&lt;p&gt;So all 100 tasks were completed in 1.5 seconds, indicating that they really were run in parallel, but all 100 were allowed to run at the same time, with no limit.&lt;&#x2F;p&gt;
&lt;p&gt;We can adjust the test program to run using our customised my_as_completed function, and pass in an iterable of coroutines instead of a list by changing the last part of the program to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;async def print_when_done(tasks):
    for res in my_as_completed(tasks):
        print(&quot;Result %s&quot; % await res)
coros = (mycoro(i) for i in range(1, 101))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_when_done(coros))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;But we get similar output to last time, with all tasks running concurrently.&lt;&#x2F;p&gt;
&lt;p&gt;To limit the number of concurrent tasks, we limit the size of the futures list, and add more as needed:&lt;&#x2F;p&gt;
&lt;pre&gt;from itertools import islice
def limited_as_completed(coros, limit):
    futures = [
        asyncio.ensure_future(c)
        for c in islice(coros, 0, limit)
    ]
    async def first_to_finish():
        while True:
            await asyncio.sleep(0)
            for f in futures:
                if f.done():
                    futures.remove(f)
                    try:
                        newf = next(coros)
                        futures.append(
                            asyncio.ensure_future(newf))
                    except StopIteration as e:
                        pass
                    return f.result()
    while len(futures) &amp;gt; 0:
        yield first_to_finish()&lt;&#x2F;pre&gt;
&lt;p&gt;We start limit tasks at first, and whenever one ends, we ask for the next coroutine in coros and set it running. This keeps the number of running tasks at or below limit until we start running out of input coroutines (when next throws and we don&#x27;t add anything to futures), then futures starts emptying until we eventually stop yielding coroutine objects.&lt;&#x2F;p&gt;
&lt;p&gt;I thought this function might be useful to others, so I started a little repo over &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;asyncioplus&quot;&gt;here&lt;&#x2F;a&gt; and added it: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;asyncioplus&#x2F;src&#x2F;branch&#x2F;master&#x2F;asyncioplus&#x2F;limited_as_completed.py&quot;&gt;asyncioplus&#x2F;limited_as_completed.py&lt;&#x2F;a&gt;. Please provide merge requests and log issues to improve it - maybe it should be part of standard Python?&lt;&#x2F;p&gt;
&lt;p&gt;When we run the same example program, but call limited_as_completed instead of the other versions:&lt;&#x2F;p&gt;
&lt;pre&gt;async def print_when_done(tasks):
    for res in &lt;span style=&quot;color:blue;&quot;&gt;limited_as_completed(tasks, 10)&lt;&#x2F;span&gt;:
        print(&quot;Result %s&quot; % await res)
coros = (mycoro(i) for i in range(1, 101))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_when_done(coros))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;We see output like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ time python3 python-async.py
Starting 1
Starting 2
...
Starting 9
Starting 10
&lt;span style=&quot;color:blue;&quot;&gt;Finishing 10&lt;&#x2F;span&gt;
Result 10
Starting 11
...
Finishing 100
Result 100
Finishing 1
Result 1

real	0m1.535s
user	0m1.436s
sys	0m0.084s&lt;&#x2F;pre&gt;
&lt;p&gt;So we can see that the tasks are still running concurrently, but this time the number of concurrent tasks is limited to 10.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;see-also&quot;&gt;See also&lt;&#x2F;h3&gt;
&lt;p&gt;To achieve a similar result using semaphores, see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;40836800&#x2F;python-asyncio-semaphore-in-async-await-function&quot;&gt;Python asyncio.semaphore in async-await function&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pawelmhm.github.io&#x2F;asyncio&#x2F;python&#x2F;aiohttp&#x2F;2016&#x2F;04&#x2F;22&#x2F;asyncio-aiohttp.html&quot;&gt;Making 1 million requests with python-aiohttp&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It feels like limited_as_completed is more re-usable as an approach but I&#x27;d love to hear others&#x27; thoughts on this. E.g. could&#x2F;should I use a semaphore to implement limited_as_completed instead of manually holding a queue?&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    David L
    &lt;span class=&quot;comment_date&quot;&gt;2017-11-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is golden, thanks for publishing it. Would love to see the &lt;code&gt;limit&lt;&#x2F;code&gt; kwarg added to &lt;code&gt;as_completed&lt;&#x2F;code&gt; in asyncio.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks David – I guess it might be worth commenting on &lt;a href=&quot;https:&#x2F;&#x2F;bugs.python.org&#x2F;issue30782&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;bugs.python.org&#x2F;issue30782&lt;&#x2F;a&gt; ?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Trahibidadido
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’ve build some kind of object realization of it. Simply to inject everywhere in my projects, much tnx.&lt;&#x2F;p&gt;
&lt;p&gt;If you’re interested, I’ll create a pull asap.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bruce Wu
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I`m developing a web crawler framework with asyncio and I met memory high usage problem in testï¼Œso thx for you blog.&lt;&#x2F;p&gt;
&lt;p&gt;I notice you used loop to polling futures completion, why not use callback function as same as official &quot;as_completed&quot; method?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Trahibidadido
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Ok, I’ve kinda lost my way and reimplement this for futures:&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;trahibidadido&#x2F;3790adf525fae08dee54555978b3c956&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;trahibidadido&#x2F;3790adf...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Trahibidadido, I’m not sure I understand.  As far as I can see a function is the nicest way to provide this – maybe you can explain a bit more?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bruce, my understanding of the official as_completed is that it is intended to be called in a loop – am I mistaken?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Trahibidadido, I think the version from my blog post will already work with futures, won’t it?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Trahibidadido
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Yes. Your solution uses only one process, and executes coros with threads (threadPoolExecutor). If you try some cpu heavy tasks (in my case many regex text transforms) – it will be slow as hell.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve tried to implement ProcessPoolExecutor and divide coros not by tasking %limit% coroutines and dealing with its futures, but tasking all coroutines at once and deal with all of the futures in processPool. This gives me some extra performance.&lt;&#x2F;p&gt;
&lt;p&gt;But my way, sadly,  it doesnt work in oop ): &lt;a href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;47662040&#x2F;cant-pickle-coroutine-objects-when-processpoolexecutor-is-used-in-class&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;47662040&#x2F;...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bruce Wu
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy, maybe my reply is not clear,  sorry for confusing youã€‚&lt;&#x2F;p&gt;
&lt;p&gt;Let`s look into your blog code:&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;    async def first_to_finish():
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;        while True:  # this is the loop I want to say
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            await asyncio.sleep(0)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;            for f in futures:
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                if f.done():
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;                    futures.remove(f)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;...
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code use a loop to capture futures&lt;code&gt;s completion, and I see the official â€œas_completedâ€ method use futures&lt;&#x2F;code&gt;s callback function.(&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;master&#x2F;Lib&#x2F;asyncio&#x2F;tasks.py#L462&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;blob&#x2F;master...&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bruce, I see that you mean &lt;em&gt;inside&lt;&#x2F;em&gt; the as_completed function, sorry!  Yes, the official version does it differently, so when I proposed to make this change in the standard library I did it that way: &lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;06...&lt;&#x2F;a&gt; – see the MR here: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;pull&#x2F;2424&#x2F;files&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;python&#x2F;cpython&#x2F;pull&#x2F;2424&#x2F;files&lt;&#x2F;a&gt; .  Personally, I prefer a for loop to callbacks, but that is a matter of taste I think.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ethan
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is fantastic. Is there any reason you wouldn’t use BoundedSemaphore for rate limiting?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;asyncio-sync.html#asyncio.BoundedSemaphore&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;asyncio-syn...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I’ve used it to rate limit aiohttp requests and it’s super clean.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-09-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Ethan, yeah that looks really useful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Multiple Async Requests Simultaneously - Programming Questions And Solutions Blog
    &lt;span class=&quot;comment_date&quot;&gt;2022-12-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] There are a number of ways to limit the rate of concurrency. For instance, see asyncio.semaphore in async-await function or large numbers of tasks with limited concurrency. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Async IO en Python: un tutorial completo - Python super fácil
    &lt;span class=&quot;comment_date&quot;&gt;2023-03-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] &lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concur%E2%80%A6&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;05...&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Basic ideas of Python 3 asyncio concurrency</title>
          <pubDate>Wed, 31 May 2017 01:46:32 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/31/basic-ideas-of-python-3-asyncio-concurrency/</link>
          <guid>https://artificialworlds.net/blog/2017/05/31/basic-ideas-of-python-3-asyncio-concurrency/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/31/basic-ideas-of-python-3-asyncio-concurrency/">&lt;p&gt;Series: asyncio basics, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;&quot;&gt;large numbers in parallel&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;12&#x2F;making-100-million-requests-with-python-aiohttp&#x2F;&quot;&gt;parallel HTTP requests&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;06&#x2F;27&#x2F;adding-a-concurrency-limit-to-pythons-asyncio-as_completed&#x2F;&quot;&gt;adding to stdlib&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Update: see the &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;02&#x2F;26&#x2F;python-async-basics-video-100-million-http-requests&#x2F;&quot;&gt;Python Async Basics video&lt;&#x2F;a&gt; on this topic.&lt;&#x2F;p&gt;
&lt;p&gt;Python 3&#x27;s asyncio module and the async and await keywords combine to allow us to do &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cooperative_multitasking&quot;&gt;cooperative concurrent programming&lt;&#x2F;a&gt;, where a code path voluntarily yields control to a scheduler, trusting that it will get control back when some resource has become available (or just when the scheduler feels like it). This way of programming can be very confusing, and has been popularised by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twistedmatrix.com&quot;&gt;Twisted&lt;&#x2F;a&gt; in the Python world, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nodejs.org&quot;&gt;nodejs&lt;&#x2F;a&gt; (among others) in other worlds.&lt;&#x2F;p&gt;
&lt;p&gt;I have been trying to get my head around the basic ideas as they surface in Python 3&#x27;s model. Below are some definitions and explanations that have been useful to me as I tried to grasp how it all works.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Futures&lt;&#x2F;strong&gt; and &lt;strong&gt;coroutines&lt;&#x2F;strong&gt; are both things that you can wait for.&lt;&#x2F;p&gt;
&lt;p&gt;You can make a coroutine by declaring it with async def:&lt;pre&gt;import asyncio
&lt;span style=&quot;color: blue;&quot;&gt;async def&lt;&#x2F;span&gt; mycoro(number):
print(&quot;Starting %d&quot; % number)
&lt;span style=&quot;color: blue;&quot;&gt;await&lt;&#x2F;span&gt; asyncio.sleep(1)
print(&quot;Finishing %d&quot; % number)
return str(number)&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Almost always, a coroutine will await something such as some blocking IO. (Above we just sleep for a second.) When we await, we actually yield control to the scheduler so it can do other work and wake us up later, when something interesting has happened.&lt;&#x2F;p&gt;
&lt;p&gt;You can make a future out of a coroutine, but often you don&#x27;t need to. Bear in mind that if you do want to make a future, you should use ensure_future, but this actually &lt;em&gt;&lt;strong&gt;runs&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt; what you pass to it - it doesn&#x27;t just create a future:&lt;pre&gt;myfuture1 = asyncio.&lt;span style=&quot;color: blue;&quot;&gt;ensure_future&lt;&#x2F;span&gt;(mycoro(1))&lt;&#x2F;p&gt;
&lt;h1 id=&quot;runs-mycoro&quot;&gt;Runs mycoro!&lt;&#x2F;pre&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;But, to get its result, you must wait for it - it is only scheduled in the background:&lt;pre&gt;# Assume mycoro is defined as above
myfuture1 = &lt;span style=&quot;color: blue;&quot;&gt;asyncio.ensure_future(mycoro(1))&lt;&#x2F;span&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;we-end-the-program-without-waiting-for-the-future-to-finish&quot;&gt;We end the program without waiting for the future to finish&lt;&#x2F;pre&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;So the above fails like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3 .&#x2F;python-async.py
Task was destroyed but it is pending!
task: &amp;lt;Task pending coro=&amp;lt;mycoro() running at .&#x2F;python-async:10&amp;gt;&amp;gt;
sys:1: RuntimeWarning: &lt;span style=&quot;color: red;&quot;&gt;coroutine &#x27;mycoro&#x27; was never awaited&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The right way to block waiting for a future outside of a coroutine is to ask the &lt;strong&gt;event loop&lt;&#x2F;strong&gt; to do it:&lt;pre&gt;# Keep on assuming mycoro is defined as above for all the examples
myfuture1 = asyncio.ensure_future(mycoro(1))
loop = asyncio.get_event_loop()
&lt;span style=&quot;color: blue;&quot;&gt;loop.run_until_complete(myfuture1)&lt;&#x2F;span&gt;
loop.close()&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now this works properly (although we&#x27;re not yet getting any benefit from being asynchronous):&lt;pre&gt;$ python3 python-async.py
Starting 1
Finishing 1&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To run several things concurrently, we make a future that is the combination of several other futures. asyncio can make a future like that out of coroutines using asyncio.gather:&lt;&#x2F;p&gt;
&lt;pre&gt;several_futures = &lt;span style=&quot;color: blue;&quot;&gt;asyncio.gather&lt;&#x2F;span&gt;(
    mycoro(1), mycoro(2), mycoro(3))
loop = asyncio.get_event_loop()
print(loop.run_until_complete(several_futures))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;The three coroutines all run at the same time, so this only takes about 1 second to run, even though we are running 3 tasks, each of which takes 1 second:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3 python-async.py
Starting 3
Starting 1
Starting 2
Finishing 3
Finishing 1
Finishing 2
[&#x27;1&#x27;, &#x27;2&#x27;, &#x27;3&#x27;]&lt;&#x2F;pre&gt;
&lt;p&gt;asyncio.gather won&#x27;t necessarily run your coroutines in order, but it will return a list of results in the same order as its input.&lt;&#x2F;p&gt;
&lt;p&gt;Notice also that run_until_complete returns the result of the future created by gather - a list of all the results from the individual coroutines.&lt;&#x2F;p&gt;
&lt;p&gt;To do the next bit we need to know how to call a coroutine from a coroutine. As we&#x27;ve already seen, just calling a coroutine in the normal Python way doesn&#x27;t run it, but gives you back a &quot;coroutine object&quot;. To actually run the code, we need to wait for it. When we want to block everything until we have a result, we can use something like run_until_complete but in an async context we want to yield control to the scheduler and let it give us back control when the coroutine has finished. We do that by using await:&lt;&#x2F;p&gt;
&lt;pre&gt;import asyncio
async def f2():
    print(&quot;start f2&quot;)
    await asyncio.sleep(1)
    print(&quot;stop f2&quot;)
async def f1():
    print(&quot;start f1&quot;)
    &lt;span style=&quot;color: blue;&quot;&gt;await&lt;&#x2F;span&gt; f2()
    print(&quot;stop f1&quot;)
loop = asyncio.get_event_loop()
loop.run_until_complete(f1())
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;This prints:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3 python-async.py
start f1
start f2
stop f2
stop f1&lt;&#x2F;pre&gt;
&lt;p&gt;Now we know how to call a coroutine from inside a coroutine, we can continue.&lt;&#x2F;p&gt;
&lt;p&gt;We have seen that asyncio.gather takes in some futures&#x2F;coroutines and returns a future that collects their results (in order).&lt;&#x2F;p&gt;
&lt;p&gt;If, instead, you want to get results as soon as they are available, you need to write a second coroutine that deals with each result by looping through the results of asyncio.as_completed and awaiting each one.&lt;&#x2F;p&gt;
&lt;pre&gt;# Keep on assuming mycoro is defined as at the top
async def print_when_done(tasks):
    for res in &lt;span style=&quot;color: blue;&quot;&gt;asyncio.as_completed&lt;&#x2F;span&gt;(tasks):
        print(&quot;Result %s&quot; % &lt;span style=&quot;color: blue;&quot;&gt;await&lt;&#x2F;span&gt; res)
coros = [mycoro(1), mycoro(2), mycoro(3)]
loop = asyncio.get_event_loop()
loop.run_until_complete(print_when_done(coros))
loop.close()&lt;&#x2F;pre&gt;
&lt;p&gt;This prints:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python3 python-async.py
Starting 1
Starting 3
Starting 2
Finishing 3
Result 3
Finishing 2
Result 2
Finishing 1
Result 1&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that task 3 finishes first and its result is printed, even though tasks 1 and 2 are still running.&lt;&#x2F;p&gt;
&lt;p&gt;asyncio.as_completed returns an iterable sequence of futures, each of which must be awaited, so it must run inside a coroutine, which must be waited for too.&lt;&#x2F;p&gt;
&lt;p&gt;The argument to asyncio.as_completed has to be a list of coroutines or futures, not an iterable, so you can&#x27;t use it with a very large list of items that won&#x27;t fit in memory.&lt;&#x2F;p&gt;
&lt;p&gt;Side note: if we want to work with very large lists, asyncio.wait won&#x27;t help us here - it also takes a list of futures and waits for all of them to complete (like gather), or, with other arguments, for one of them to complete or one of them to fail. It then returns two sets of futures: done and not-done. Each of these must be awaited to get their results, so:&lt;&#x2F;p&gt;
&lt;pre&gt;asyncio.gather

# is roughly equivalent to:

async def mygather(*args):
    ret = []
    for r in (await asyncio.wait(args))[0]:
        ret.append(await r)
    return ret&lt;&#x2F;pre&gt;
&lt;p&gt;Further reading: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;realpython.com&#x2F;async-io-python&#x2F;&quot;&gt;realpython.com&#x2F;async-io-python&lt;&#x2F;a&gt; (a very complete and clear explanation, with lots of links)&lt;&#x2F;p&gt;
&lt;p&gt;I am interested in running very large numbers of tasks with limited concurrency - see the &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;31&#x2F;python-3-large-numbers-of-tasks-with-limited-concurrency&#x2F;&quot;&gt;next article&lt;&#x2F;a&gt; for how I managed it.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Attila Homoki
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Just what I was looking for, very nicely presented…thank you&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Attila!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dmitry
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy,&lt;&#x2F;p&gt;
&lt;p&gt;Your article explains the topic in very clear and neat way.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you very much.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-02-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Dmitry, glad it helped.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Hamad
    &lt;span class=&quot;comment_date&quot;&gt;2018-04-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wow dude. I think i might finally be able to understand async thanks to this brilliant article. Thanks a ton&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-04-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great to hear it Hamad!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Kansai
    &lt;span class=&quot;comment_date&quot;&gt;2020-05-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you for the article. I learned a lot. I have a question though.&lt;br&#x2F;&gt;
In the function&lt;br&#x2F;&gt;
async def print_when_done(tasks):&lt;br&#x2F;&gt;
for res in asyncio.as_completed(tasks):&lt;br&#x2F;&gt;
print(&quot;Result %s&quot; % await res)&lt;br&#x2F;&gt;
print(&quot;Argument is&quot;) #Here&lt;&#x2F;p&gt;
&lt;p&gt;is there a way to know the argument that is being passed?&lt;br&#x2F;&gt;
For example since I am passing&lt;br&#x2F;&gt;
coros = [mycoro(1), mycoro(2), mycoro(3)]&lt;br&#x2F;&gt;
can I somehow print !Argument is 1&quot; &quot;Argument is 2&quot; &quot;Argument is 3&quot; somehow?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-06-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Kansai, I don’t think so – you’ll need to print the argument inside mycoro.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    palau
    &lt;span class=&quot;comment_date&quot;&gt;2021-03-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy
your artical is very good
Thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>C++ iterator wrapping a stream not 1-1</title>
          <pubDate>Wed, 17 May 2017 07:25:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/17/c-iterator-wrapping-a-stream-not-1-1/</link>
          <guid>https://artificialworlds.net/blog/2017/05/17/c-iterator-wrapping-a-stream-not-1-1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/17/c-iterator-wrapping-a-stream-not-1-1/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;11&#x2F;c-iterator-example-and-an-iterable-range&#x2F;&quot;&gt;Iterator&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;12&#x2F;c-iterator-wrapperadaptor-example&#x2F;&quot;&gt;Iterator Wrapper&lt;&#x2F;a&gt;, Non-1-1 Wrapper&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes we want to write an iterator that consumes items from some underlying iterator but produces its own items slower than the items it consumes, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;ColonSep items(&quot;aa:foo::x&quot;);
&#x2F;&#x2F; Prints &quot;aa, foo, , x&quot;
for(auto s : items)
{
    std::cout &amp;lt;&amp;lt; s &amp;lt;&amp;lt; &quot;, &quot;;
}&lt;&#x2F;pre&gt;
&lt;p&gt;When we pass a 9-character string (i.e. an iterator that yields 9 items) to ColonSep, above, we only repeat 4 times in our loop, because ColonSep provides an iterable range that yields one value for each whole word it finds in the underlying iterator of 9 characters.&lt;&#x2F;p&gt;
&lt;p&gt;To do something like this I&#x27;d recommend consuming the items of the underlying iterator early, so it is ready when requested with operator*. We also need our iterator class to hold on to the end of the underlying iterator as well as the current position.&lt;&#x2F;p&gt;
&lt;p&gt;First we need a small container to hold the next item we will provide:&lt;&#x2F;p&gt;
&lt;pre&gt;struct maybestring
{
    std::string value_;
    bool at_end_;

    explicit maybestring(const std::string value)
    : value_(value)
    , at_end_(false)
    {}

    explicit maybestring()
    : value_(&quot;--error-past-end--&quot;)
    , at_end_(true)
    {}
};&lt;&#x2F;pre&gt;
&lt;p&gt;A maybestring either holds the next item we will provide, or at_end_ is true, meaning we have reached the end of the underlying iterator and we will report that we are at the end ourself when asked.&lt;&#x2F;p&gt;
&lt;p&gt;Like the simpler iterators we have looked at, we still need a little container to return from the postincrement operator:&lt;&#x2F;p&gt;
&lt;pre&gt;class stringholder
{
    const std::string value_;
public:
    stringholder(const std::string value) : value_(value) {}
    std::string operator*() const { return value_; }
};&lt;&#x2F;pre&gt;
&lt;p&gt;Now we are ready to write our iterator class, which always has the next value ready in its next_ member, and holds on to the current and end positions of the underlying iterator in wrapped_ and wrapped_end_:&lt;&#x2F;p&gt;
&lt;pre&gt;class myit
{
private:
    typedef std::string::const_iterator wrapped_t;
    wrapped_t wrapped_;
    wrapped_t wrapped_end_;
    maybestring next_;&lt;&#x2F;pre&gt;
&lt;p&gt;The constructor holds on the underlying iterator pointers, and immediately fills next_ with the next value by calling next_item passing in true to indicate that this is the first item:&lt;&#x2F;p&gt;
&lt;pre&gt;public:
    myit(wrapped_t wrapped, wrapped_t wrapped_end)
    : wrapped_(wrapped)
    , wrapped_end_(wrapped_end)
    , next_(next_item(true))
    {
    }

    &#x2F;&#x2F; Previously provided by std::iterator
    typedef int                     value_type;
    typedef std::ptrdiff_t          difference_type;
    typedef int*                    pointer;
    typedef int&amp;amp;                    reference;
    typedef std::input_iterator_tag iterator_category;&lt;&#x2F;pre&gt;
&lt;p&gt;next_item looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;private:
    maybestring next_item(bool first_time)
    {
        if (wrapped_ == wrapped_end_)
        {
            return maybestring();  &#x2F;&#x2F; We are at the end
        }
        else
        {
            if (!first_time)
            {
                ++wrapped_;
            }
            return read_item();
        }
    }&lt;&#x2F;pre&gt;
&lt;p&gt;next_item recognises whether we&#x27;ve reached the end of the underlying iterator and saves the empty maybstring if so. Otherwise, it skips forward once (unless we are on the first element) and then calls read_item:&lt;&#x2F;p&gt;
&lt;pre&gt;    maybestring read_item()
    {
        std::string ret = &quot;&quot;;
        for (; wrapped_ != wrapped_end_; ++wrapped_)
        {
            char c = *wrapped_;
            if (c == &#x27;:&#x27;)
            {
                break;
            }
            ret += c;
        }
        return maybestring(ret);
    }&lt;&#x2F;pre&gt;
&lt;p&gt;read_item implements the real logic of looping through the underlying iterator and combining those values together to create the next item to provide.&lt;&#x2F;p&gt;
&lt;p&gt;The hard part of the iterator class is done, leaving only the more normal functions we must provide:&lt;&#x2F;p&gt;
&lt;pre&gt;public:
    value_type operator*() const
    {
        assert(!next_.at_end_);
        return next_.value_;
    }

    bool operator==(const myit&amp;amp; other) const
    {
        &#x2F;&#x2F; We only care about whether we are at the end
        return next_.at_end_ == other.next_.at_end_;
    }

    bool operator!=(const myit&amp;amp; other) const { return !(*this == other); }

    stringholder operator++(int)
    {
        assert(!next_.at_end_);
        stringholder ret(next_.value_);
        next_ = next_item(false);
        return ret;
    }

    myit&amp;amp; operator++()
    {
        assert(!next_.at_end_);
        next_ = next_item(false);
        return *this;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Note that operator== is only concerned with whether or not we are an end iterator or not. Nothing else matters for providing correct iteration.&lt;&#x2F;p&gt;
&lt;p&gt;Our final bit of bookkeeping is the range class that allows our new iterator to be used in a for loop:&lt;&#x2F;p&gt;
&lt;pre&gt;class ColonSep
{
private:
    const std::string str_;
public:
    ColonSep(const std::string str) : str_(str) {}
    myit begin() { return myit(std::begin(str_), std::end(str_)); }
    myit end()   { return myit(std::end(str_),   std::end(str_)); }
};&lt;&#x2F;pre&gt;
&lt;p&gt;A lot of the code above is needed for all code that does this kind of job. Next time we&#x27;ll look at how to use templates to make it useable in the general case.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to write a programming language ACCU talk</title>
          <pubDate>Wed, 17 May 2017 00:08:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/17/how-to-write-a-programming-language-accu-talk/</link>
          <guid>https://artificialworlds.net/blog/2017/05/17/how-to-write-a-programming-language-accu-talk/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/17/how-to-write-a-programming-language-accu-talk/">&lt;p&gt;My talk from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;channel&#x2F;UCJhay24LTpO1s4bIZxuIqKw&quot;&gt;ACCU Conference 2017&lt;&#x2F;a&gt; where I describe a tiny programming language I wrote:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=82-XjMzKaC8&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;82-XjMzKaC8&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;how-to-write-a-programming-language&#x2F;how-to-write-a-programming-language.html&quot;&gt;How to write a programming language&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Cell source code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&quot;&gt;github.com&#x2F;andybalaam&#x2F;cell&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>C++ iterator wrapper&#x2F;adaptor example</title>
          <pubDate>Fri, 12 May 2017 07:10:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/12/c-iterator-wrapperadaptor-example/</link>
          <guid>https://artificialworlds.net/blog/2017/05/12/c-iterator-wrapperadaptor-example/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/12/c-iterator-wrapperadaptor-example/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;11&#x2F;c-iterator-example-and-an-iterable-range&#x2F;&quot;&gt;Iterator&lt;&#x2F;a&gt;, Iterator Wrapper, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;17&#x2F;c-iterator-wrapping-a-stream-not-1-1&#x2F;&quot;&gt;Non-1-1 Wrapper&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to wrap an iterable range with another that transforms the underlying iterators in some way and allows looping or constructing other objects:&lt;&#x2F;p&gt;
&lt;pre&gt;for (auto ch : Upper(&quot;abcdef&quot;))
{
    &#x2F;&#x2F; Prints &quot;ABCDEF&quot;
    std::cout &amp;lt;&amp;lt; ch;
}
Upper up(std::string(&quot;fOo&quot;));
std::string newfoo(std::begin(up), std::end(up));
assert(newfoo == &quot;FOO&quot;);
&lt;&#x2F;pre&gt;
&lt;p&gt;then, similar to &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;11&#x2F;c-iterator-example-and-an-iterable-range&#x2F;&quot;&gt;an ordinary iterable range&lt;&#x2F;a&gt; you will need to make a range class and a iterator class:&lt;&#x2F;p&gt;
&lt;pre&gt;class myit
{
private:
    std::string::const_iterator wrapped_;
    class charholder
    {
        const char value_;
    public:
        charholder(const char value) : value_(value) {}
        char operator*() const { return value_; }
    };
public:
    &#x2F;&#x2F; Previously provided by std::iterator
    typedef int                     value_type;
    typedef std::ptrdiff_t          difference_type;
    typedef int*                    pointer;
    typedef int&amp;amp;                    reference;
    typedef std::input_iterator_tag iterator_category;

    explicit myit(std::string::const_iterator wrapped) : wrapped_(wrapped) {}
    value_type operator*() const { return std::toupper(*wrapped_); }
    bool operator==(const myit&amp;amp; other) const { return wrapped_ == other.wrapped_; }
    bool operator!=(const myit&amp;amp; other) const { return !(*this == other); }
    charholder operator++(int)
    {
        charholder ret(std::toupper(*wrapped_));
        ++wrapped_;
        return ret;
    }
    myit&amp;amp; operator++()
    {
        ++wrapped_;
        return *this;
    }
};

class Upper
{
private:
    const std::string str_;
public:
    Upper(const std::string str) : str_(str) {}
    myit begin() { return myit(std::begin(str_)); }
    myit end()   { return myit(std::end(str_)); }
};
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice the need to call the transforming&#x2F;adapting functionstd::toupper in two places.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; std::iterator is deprecated in C++17, so removed.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ramyken
    &lt;span class=&quot;comment_date&quot;&gt;2019-10-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello Andy,&lt;br&#x2F;&gt;
firstly many thanks for the great adaptor example. But what I don’t understand is why you have implemented the inner class charholder used in charholder operator++(int). Is it really necessary? Thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-10-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Ramyken, Thanks!  See the comments on the first post in this series here: &lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;05&#x2F;11&#x2F;c-iterator-example-and-an-iterable-range&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2017&#x2F;05...&lt;&#x2F;a&gt; .  I thought very similarly to you when I wrote that, and Anthony Williams (author of C++ Concurrency In Action) very kindly put me straight.  The postincrement operator has to return the value at the iterator before it was incremented.  (That is why, in general, you should not use it…).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ramyken
    &lt;span class=&quot;comment_date&quot;&gt;2019-10-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy, many thanks, I did not know about it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>C++ iterator example (and an iterable range)</title>
          <pubDate>Thu, 11 May 2017 07:15:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/11/c-iterator-example-and-an-iterable-range/</link>
          <guid>https://artificialworlds.net/blog/2017/05/11/c-iterator-example-and-an-iterable-range/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/11/c-iterator-example-and-an-iterable-range/">&lt;p&gt;Series: Iterator, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;12&#x2F;c-iterator-wrapperadaptor-example&#x2F;&quot;&gt;Iterator Wrapper&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2017&#x2F;05&#x2F;17&#x2F;c-iterator-wrapping-a-stream-not-1-1&#x2F;&quot;&gt;Non-1-1 Wrapper&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To make your own iterable range in C++ that you can loop over or make a vector out of (mine is called Numbers):&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; Prints:
&#x2F;&#x2F; 3,4,
for (auto n : Numbers(3, 5))
{
    std::cout &amp;lt;&amp;lt; n &amp;lt;&amp;lt; &quot;,&quot;;
}

&#x2F;&#x2F; Fills vec with 7, 8, 9
Numbers nums(7, 10);
std::vector&lt;int&gt; vec{std::begin(nums), std::end(nums)};
&lt;&#x2F;int&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;you need to write a class that is an Input Iterator, and provide an instance as the begin and end of your range:&lt;&#x2F;p&gt;
&lt;pre&gt;class Numbers
{
private:
    const int start_;
    const int end_;
public:
    Numbers(int start, int end) : start_(start) , end_(end) {}
    myit begin() { return myit(start_); }
    myit end()   { return myit(end_); }
};
&lt;&#x2F;pre&gt;
&lt;p&gt;The hard bit is the Input Iterator:&lt;&#x2F;p&gt;
&lt;pre&gt;#include &amp;lt;iterator&amp;gt;

class myit
{
private:
    int value_;
    class intholder
    {
        int value_;
    public:
        intholder(int value): value_(value) {}
        int operator*() { return value_; }
    };
public:
    &#x2F;&#x2F; Previously provided by std::iterator - see update below
    typedef int                     value_type;
    typedef std::ptrdiff_t          difference_type;
    typedef int*                    pointer;
    typedef int&amp;amp;                    reference;
    typedef std::input_iterator_tag iterator_category;

    explicit myit(int value) : value_(value) {}
    int operator*() const { return value_; }
    bool operator==(const myit&amp;amp; other) const { return value_ == other.value_; }
    bool operator!=(const myit&amp;amp; other) const { return !(*this == other); }
    intholder operator++(int)
    {
        intholder ret(value_);
        ++*this;
        return ret;
    }
    myit&amp;amp; operator++()
    {
        ++value_;
        return *this;
    }
};
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: thanks to Anthony Williams for the correction on the postincrement operator - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.justsoftwaresolutions.co.uk&#x2F;cplusplus&#x2F;generating_sequences.html&quot;&gt;Generating Sequences&lt;&#x2F;a&gt; for more. Note the need to return a fiddly object that holds the answer we will give when the return value is dereferenced.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;del&gt;Using std::iterator as a base class actually only gives you some typedefs for free, but it&#x27;s worth it to get the standard names, and to be clear what we are trying to do.&lt;&#x2F;del&gt; &lt;strong&gt;Update:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.open-std.org&#x2F;jtc1&#x2F;sc22&#x2F;wg21&#x2F;docs&#x2F;papers&#x2F;2016&#x2F;p0174r1.html#2.1&quot;&gt;std::iterator is deprecated in C++17&lt;&#x2F;a&gt; - just add the 5 typedefs yourself.&lt;&#x2F;p&gt;
&lt;p&gt;I suspect I might need to add some extra &amp;amp;s to make this good C++11 style?&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anthony Williams
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Sadly, your postincrement operator isn’t right.&lt;&#x2F;p&gt;
&lt;p&gt;The requirement on postincrement is that *it++ returns the value at the iterator before the increment. Returning value_type from the postincrement operator doesn’t work for that: you would need to write just it++, which would be odd.&lt;&#x2F;p&gt;
&lt;p&gt;I explained how to write an iterator for exactly this scenario (numeric ranges) on my blog a couple of months ago: &lt;a href=&quot;https:&#x2F;&#x2F;www.justsoftwaresolutions.co.uk&#x2F;cplusplus&#x2F;generating_sequences.html&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.justsoftwaresolutions.co.uk&#x2F;cplus...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Anthony – I wish I’d found your post when I was researching this.  I was kind of hoping I’d get some corrections if I posted this!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Make Android Gradle display unit test failure messages</title>
          <pubDate>Wed, 10 May 2017 00:30:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/10/make-android-gradle-display-unit-test-failure-messages/</link>
          <guid>https://artificialworlds.net/blog/2017/05/10/make-android-gradle-display-unit-test-failure-messages/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/10/make-android-gradle-display-unit-test-failure-messages/">&lt;p&gt;By default, Gradle does not show you what happened when a unit test failed:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;gradlew test
...
MyTest &amp;gt; Black_is_white FAILED
    org.junit.ComparisonFailure at MyTest.java:6
    &lt;span style=&quot;color:red&quot;&gt;^^^ WHAT ACTUALLY FAILED????&lt;&#x2F;span&gt;
...&lt;&#x2F;pre&gt;
&lt;p&gt;This is insane, and can be fixed (thanks to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mrhaki.blogspot.co.uk&#x2F;2013&#x2F;05&#x2F;gradle-goodness-show-more-information.html&quot;&gt;mrhaki&lt;&#x2F;a&gt;) by editing build.gradle to add:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; NOTE: this is the &lt;span style=&quot;color: blue;&quot;&gt;non-Android&lt;&#x2F;span&gt; solution - add to build.gradle
test {
    testLogging {
        exceptionFormat = &#x27;full&#x27;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;The above doesn&#x27;t work with Android, but something similar does:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; &lt;span style=&quot;color: blue;&quot;&gt;Android&lt;&#x2F;span&gt; solution: add this to app&#x2F;build.gradle
android.testOptions.unitTests.all {
    testLogging {
        exceptionFormat = &quot;full&quot;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;And sanity prevails:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;gradlew test
...
MyTest &amp;gt; Black_is_white FAILED
    org.junit.ComparisonFailure:
    expected:&amp;lt;[black]&amp;gt; but was:&amp;lt;[white]&amp;gt;
        at org.junit.Assert.assertEquals(Assert.java:115)
        at org.junit.Assert.assertEquals(Assert.java:144)
        at MyTest.Black_is_white(MyTest.java:6)
...&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;files-for-plain-gradle&quot;&gt;Files for plain Gradle:&lt;&#x2F;h3&gt;
&lt;pre&gt;$ &lt;span style=&quot;color: blue;&quot;&gt;cat build.gradle&lt;&#x2F;span&gt;
apply plugin: &#x27;java&#x27;

repositories {
    mavenCentral()
}

dependencies {
    testCompile &#x27;junit:junit:[4,)&#x27;
}

test {
    testLogging {
        exceptionFormat = &#x27;full&#x27;
    }
}

$ &lt;span style=&quot;color: blue;&quot;&gt;cat src&#x2F;test&#x2F;java&#x2F;MyTest.java&lt;&#x2F;span&gt;
public class MyTest
{
    @org.junit.Test
    public void Black_is_white()
    {
        org.junit.Assert.assertEquals(&quot;black&quot;, &quot;white&quot;);
    }
}&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;files-for-android-gradle&quot;&gt;Files for Android+Gradle&lt;&#x2F;h3&gt;
&lt;pre&gt;$ &lt;span style=&quot;color: blue;&quot;&gt;cat build.gradle&lt;&#x2F;span&gt;
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath &#x27;com.android.tools.build:gradle:2.3.1&#x27;
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

$ &lt;span style=&quot;color: blue;&quot;&gt;cat app&#x2F;src&#x2F;main&#x2F;AndroidManifest.xml&lt;&#x2F;span&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;manifest xmlns:android=&quot;http:&#x2F;&#x2F;schemas.android.com&#x2F;apk&#x2F;res&#x2F;android&quot;
    package=&quot;net.artificialworlds.testapp&quot;&amp;gt;
&amp;lt;&#x2F;manifest&amp;gt;

$ &lt;span style=&quot;color: blue;&quot;&gt;cat app&#x2F;src&#x2F;test&#x2F;java&#x2F;MyTest.java&lt;&#x2F;span&gt;
public class MyTest
{
    @org.junit.Test
    public void Black_is_white()
    {
        org.junit.Assert.assertEquals(&quot;black&quot;, &quot;white&quot;);
    }
}

$ &lt;span style=&quot;color: blue;&quot;&gt;cat build.gradle&lt;&#x2F;span&gt;
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath &#x27;com.android.tools.build:gradle:2.3.1&#x27;
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

$ &lt;span style=&quot;color: blue;&quot;&gt;cat local.properties&lt;&#x2F;span&gt;
sdk.dir=&#x2F;home&#x2F;andy&#x2F;Android&#x2F;Sdk

$ &lt;span style=&quot;color: blue;&quot;&gt;cat settings.gradle&lt;&#x2F;span&gt;
include &#x27;:app&#x27;

&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Planet Code updates</title>
          <pubDate>Tue, 02 May 2017 21:12:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/02/planet-code-updates/</link>
          <guid>https://artificialworlds.net/blog/2017/05/02/planet-code-updates/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/02/planet-code-updates/">&lt;p&gt;I maintain an unofficial aggregator of blogs by people who are involved with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;accu.org&#x2F;&quot;&gt;ACCU&lt;&#x2F;a&gt; and things they might be interested in.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;planetcode&#x2F;&quot;&gt;Planet Code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Today I made it look slightly less ugly, and added several new blogs after attending the excellent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;conference.accu.org&quot;&gt;ACCU Conference&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Subscribe, and enjoy.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A story about magic and how we treat each other</title>
          <pubDate>Tue, 02 May 2017 08:29:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/05/02/a-story-about-magic-and-how-we-treat-each-other/</link>
          <guid>https://artificialworlds.net/blog/2017/05/02/a-story-about-magic-and-how-we-treat-each-other/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/05/02/a-story-about-magic-and-how-we-treat-each-other/">&lt;p&gt;For a lightning talk at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;conference.accu.org&#x2F;&quot;&gt;ACCU Conference&lt;&#x2F;a&gt; I wrote a little story:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;presentations&#x2F;a-story-about-feeling-safe&#x2F;a-story.html&quot;&gt;A story about magic and how we treat each other&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It describes one person&#x27;s journey towards realising that we need to act to be kind to each other, and not to expect it to happen automatically.&lt;&#x2F;p&gt;
&lt;p&gt;In the tech community, a lot of people want to be kind, but find relating to people hard work, which can mean people are excluded when we stick to familiar groups. I think there was a widespread opinion that proper geeks were so uninterested in relationship stuff that we treated everyone equally by default, but that is clearly untrue given the makeup of many tech communities.&lt;&#x2F;p&gt;
&lt;p&gt;People are being excluded, and we need to be proactive in changing the behaviours that cause this.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Annual cost of clean water for every human</title>
          <pubDate>Tue, 07 Mar 2017 15:53:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/03/07/annual-cost-of-clean-water-for-every-human/</link>
          <guid>https://artificialworlds.net/blog/2017/03/07/annual-cost-of-clean-water-for-every-human/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/03/07/annual-cost-of-clean-water-for-every-human/">&lt;p&gt;According to a 2010 &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.nytimes.com&#x2F;2000&#x2F;11&#x2F;23&#x2F;world&#x2F;price-of-safe-water-for-all-10-billion-and-the-will-to-provide-it.html&quot;&gt;WHO-backed study&lt;&#x2F;a&gt; the cost of safe water for every human would be about 10 billion US dollars per year.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.theguardian.com&#x2F;news&#x2F;datablog&#x2F;2010&#x2F;apr&#x2F;25&#x2F;tax-receipts-1963&quot;&gt;UK government revenue for 2010-2011&lt;&#x2F;a&gt; was about 639 billion US dollars.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;ten-billion.svg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I would vote for a political party proposing to raise this money and spend it for this purpose.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Iterating over the lines of a file in Java</title>
          <pubDate>Thu, 09 Feb 2017 13:25:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/02/09/iterating-over-the-lines-of-a-file-in-java/</link>
          <guid>https://artificialworlds.net/blog/2017/02/09/iterating-over-the-lines-of-a-file-in-java/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/02/09/iterating-over-the-lines-of-a-file-in-java/">&lt;p&gt;If you&#x27;re trying to write a standard command-line tool in Java you are going to experience pain.&lt;&#x2F;p&gt;
&lt;p&gt;In Python, if we want to do something with each line on input to your program, we can do something like:&lt;&#x2F;p&gt;
&lt;pre&gt;import sys
for ln in sys.stdin:
    print(ln)  # (We have an extra newline but who&#x27;s counting?)
&lt;&#x2F;pre&gt;
&lt;p&gt;In Java, after some fighting, the two closest alternatives I can find are:&lt;&#x2F;p&gt;
&lt;pre&gt;import java.util.Scanner;
class IterateInputScanner {
    public static void main(String[] args) {
        for(String ln : (Iterable&lt;string&gt;)(() -&amp;gt; new Scanner(System.in).useDelimiter(&quot;\n&quot;))) {
            System.out.println(ln);
        }
    }
}
&lt;&#x2F;string&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;or, less fashionably, and even more horribly:&lt;&#x2F;p&gt;
&lt;pre&gt;import java.io.BufferedReader;
import java.io.InputStreamReader;
class IterateInputReader {
    public static void main(String[] args) throws java.io.IOException {
        BufferedReader reader =
            new BufferedReader(new InputStreamReader(System.in));
        String ln;
        while((ln = reader.readLine()) != null) {
            System.out.println(ln);
        }
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Note that if you are reading from a stream other than stdin you will probably want to wrap this in a try-with-resources to close the stream when you&#x27;ve finished.&lt;&#x2F;p&gt;
&lt;p&gt;Take your pick, and I hope it saves you some fighting.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Scott Shipp
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Check out java.nio.file.Files and java.nio.file.Paths. Something like:&lt;&#x2F;p&gt;
&lt;p&gt;Files.lines(Paths.get(fileToRead)).forEach((line) -&amp;gt; {&lt;br&#x2F;&gt;
System.out.println(line);&lt;br&#x2F;&gt;
} );&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Nice, thanks Scott!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Raspberry Pi Jam &quot;Chaos Car!&quot;</title>
          <pubDate>Sun, 22 Jan 2017 13:01:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/01/22/raspberry-pi-jam-chaos-car/</link>
          <guid>https://artificialworlds.net/blog/2017/01/22/raspberry-pi-jam-chaos-car/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/01/22/raspberry-pi-jam-chaos-car/">&lt;p&gt;Raspberry Pi 1&lt;br &#x2F;&gt;
+ battery pack&lt;br &#x2F;&gt;
+ Bluetooth USB dongle&lt;br &#x2F;&gt;
+ i-racer bluetooth car&lt;br &#x2F;&gt;
+ Raspberry Pi camera&lt;br &#x2F;&gt;
+ some Python code&lt;br &#x2F;&gt;
+ loads of sellotape&lt;br &#x2F;&gt;
=&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;20170121_154747.jpg&quot; alt=&quot;&quot; width=&quot;800&quot; height=&quot;600&quot; class=&quot;alignnone size-full wp-image-2071&quot;&gt;
&lt;p&gt;Chaos car!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the code:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python2

import os
import random
import bluetooth
import sys
import time

car_name = &quot;DaguCar&quot;

def find_car_mac( car_name ):
    ret = None

    print &quot;Scanning for a device called %s...&quot; % car_name

    devices = bluetooth.discover_devices()

    for addr in devices:
        dev_name = bluetooth.lookup_name( addr )
        if dev_name == car_name:
            print &quot;Car found!  (MAC=%s)&quot; % addr
            time.sleep( 1 )
            return addr
        else:
            print &quot;Skipping device named &#x27;%s&#x27;.&quot; % dev_name

    sys.stderr.write( &quot;Couldn&#x27;t find a device called %s!\n&quot; % car_name )
    sys.exit( 1 )

#car_mac = find_car_mac( car_name )

print &quot;Using hard-coded MAC address&quot;
car_mac = &quot;20:13:04:23:05:71&quot;

print &quot;Connecting to the car at %s...&quot; % car_mac

sock = bluetooth.BluetoothSocket( bluetooth.RFCOMM )
sock.connect( ( car_mac, 1 ) )

class Car:
    def turn_right(self):
        print(&quot;looking right&quot;)
        sock.send( &#x27;\x6A&#x27; )
    def turn_left(self):
        print(&quot;looking left&quot;)
        sock.send( &#x27;\x5A&#x27; )
    def roll_forward(self):
        print(&quot;watch out in front coming forward&quot;)
        sock.send( &#x27;\x1A&#x27; )
    def roll_backward(self):
        print(&quot;beep beep going backward&quot;)
        sock.send( &#x27;\x2A&#x27; )
    def wait(self):
        print(&quot;waiting&quot;)
        sock.send( &#x27;\x00&#x27; )

car = Car()

instruction = [&quot;turn right&quot;, &quot;turn left&quot;, &quot;roll forward&quot;, &quot;roll backward&quot;, &quot;wait&quot;]

while True:
    ci = random.choice(instruction)
    if ci == &quot;turn right&quot;:
        car.turn_right()
    elif ci == &quot;turn left&quot;:
        car.turn_left()
    elif ci == &quot;roll forward&quot;:
        car.roll_forward()
    elif ci == &quot;roll backward&quot;:
        car.roll_backward()
    elif ci == &quot;wait&quot;:
        car.wait()
    time.sleep(0.5)
&lt;&#x2F;pre&gt;
&lt;p&gt;Improving the code to avoid bumping into walls is left as an exercise for the reader.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Raspberry Pi Jam â€œChaos Car!â€ Code #piday #raspberrypi @Raspberry_Pi « Adafruit Industries – Makers, hackers, artists, designers and engineers!
    &lt;span class=&quot;comment_date&quot;&gt;2017-01-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] From Andy Balaam’s Blog: […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Automated UI tests on Android</title>
          <pubDate>Tue, 17 Jan 2017 22:37:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/01/17/automated-ui-tests-on-android/</link>
          <guid>https://artificialworlds.net/blog/2017/01/17/automated-ui-tests-on-android/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/01/17/automated-ui-tests-on-android/">&lt;p&gt;I recently fought the Android emulator a lot to get my UI tests to run automatically during the build of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;, so I thought I&#x27;d better write down what I did before I forget.&lt;&#x2F;p&gt;
&lt;p&gt;I already have tests that drive the Android UI (see e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;src&#x2F;branch&#x2F;master&#x2F;android&#x2F;app&#x2F;src&#x2F;androidTest&#x2F;java&#x2F;rabbitescape&#x2F;ui&#x2F;android&#x2F;SmokeTest.java&quot;&gt;SmokeTest.java&lt;&#x2F;a&gt; - it&#x27;s not pretty, but it seems reliable, and it catches real problems) - this post is about how to run them automatically during our build.&lt;&#x2F;p&gt;
&lt;p&gt;Note that to run the Android emulator I&#x27;m fairly sure you need a windowing environment, so I don&#x27;t think this could be moved to a headless build server. If course, you could always fight some kind of framebuffer thing.&lt;&#x2F;p&gt;
&lt;p&gt;Here is the part of our Makefile that launches the tests:&lt;pre&gt;android-smoke-tests:
@echo &quot;. Running Android smoke tests&quot;
.&#x2F;build-scripts&#x2F;android-start-emulator &quot;android-8&quot; &quot;3.2in QVGA (ADP2)&quot;
.&#x2F;build-scripts&#x2F;android-test &quot;free&quot; &quot;app-free-debug&quot;
.&#x2F;build-scripts&#x2F;android-test &quot;&quot; &quot;app-paid-debug&quot;
.&#x2F;build-scripts&#x2F;android-stop-emulator
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and here is .&#x2F;build-scripts&#x2F;android-start-emulator - it starts up and emulator, waits for it to be ready, and unlocks its screen.:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -x
set -u
set -e

# Args

TARGET=&quot;$1&quot;   # E.g. &quot;android-8&quot;
DEVICE=&quot;$2&quot;   # E.g. &quot;3.2in QVGA (ADP2)&quot;

# Setup

ADB=&quot;${HOME}&#x2F;Android&#x2F;Sdk&#x2F;platform-tools&#x2F;adb&quot;
EMULATOR=&quot;${HOME}&#x2F;Android&#x2F;Sdk&#x2F;tools&#x2F;emulator&quot;
ANDROID=&quot;${HOME}&#x2F;Android&#x2F;Sdk&#x2F;tools&#x2F;android&quot;
NAME=&quot;rabbitescape-${TARGET}&quot;
TMP=&quot;&#x2F;data&#x2F;local&#x2F;tmp&quot;

${ANDROID} create avd \
    --force \
    --name &quot;${NAME}&quot; \
    --target &quot;${TARGET}&quot; \
    --abi &quot;armeabi&quot; \
    --device &quot;${DEVICE}&quot;

# Start the emulator
${EMULATOR} -avd &quot;${NAME}&quot; &amp;amp;

# Wait for the device to boot and unlock it
${ADB} wait-for-device shell &amp;lt;&lt;endscript echo=&quot;&quot; -n=&quot;&quot; &quot;waiting=&quot;&quot; for=&quot;&quot; device=&quot;&quot; to=&quot;&quot; boot=&quot;&quot; &quot;=&quot;&quot; &quot;&quot;=&quot;&quot;&gt; ${TMP}&#x2F;zero
getprop dev.bootcomplete &amp;gt; ${TMP}&#x2F;bootcomplete
while cmp ${TMP}&#x2F;zero ${TMP}&#x2F;bootcomplete; do
{
    echo -n &quot;.&quot;
    sleep 1
    getprop dev.bootcomplete &amp;gt; ${TMP}&#x2F;bootcomplete
}; done
echo &quot;Booted.&quot;
exit
ENDSCRIPT

echo &quot;Waiting 30 secs for us to be really booted&quot;
sleep 30

echo &quot;Unlocking screen&quot;
${ADB} shell &quot;input keyevent 82&quot;
&lt;&#x2F;endscript&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now here is android-test - it launches the JUnit test code on the running emulator::&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -x
set -u
set -e

PKGSUFFIX=&quot;$1&quot;   # E.g. &quot;free&quot;
APKNAME=&quot;$2&quot;     # E.g. &quot;app-free-debug&quot;

APPID=&quot;net.artificialworlds.rabbitescape${PKGSUFFIX}&quot;
TESTAPPID=&quot;net.artificialworlds.rabbitescape${PKGSUFFIX}.test&quot;
APK=&quot;rabbit-escape-ui-android&#x2F;app&#x2F;build&#x2F;outputs&#x2F;apk&#x2F;${APKNAME}.apk&quot;
TESTAPK=&quot;rabbit-escape-ui-android&#x2F;app&#x2F;build&#x2F;outputs&#x2F;apk&#x2F;${APKNAME}-androidTest.apk&quot;
ADB=&quot;${HOME}&#x2F;Android&#x2F;Sdk&#x2F;platform-tools&#x2F;adb&quot;
DIR=&quot;&#x2F;data&#x2F;local&#x2F;tmp&#x2F;${APPID}&quot;
TESTDIR=&quot;&#x2F;data&#x2F;local&#x2F;tmp&#x2F;${TESTAPPID}&quot;

function run_test()
{
    TMPFILE=$(mktemp)

    ${ADB} shell am instrument \
        -w \
        -r \
        -e class &quot;$1&quot; \
        &quot;${TESTAPPID}&#x2F;android.test.InstrumentationTestRunner&quot; \
    | tee ${TMPFILE}

    egrep &quot;OK (.* tests?)&quot; ${TMPFILE}
}

${ADB} push &quot;${APK}&quot; &quot;${DIR}&quot;
${ADB} push &quot;${TESTAPK}&quot; &quot;${TESTDIR}&quot;

${ADB} shell pm install -r &quot;${DIR}&quot;
${ADB} shell pm install -r &quot;${TESTDIR}&quot;

run_test rabbitescape.ui.android.DialogsTest
run_test rabbitescape.ui.android.SmokeTest
run_test rabbitescape.ui.android.TestAndroidConfigUpgradeTo1
&lt;&#x2F;pre&gt;
&lt;p&gt;And here is android-stop-emulator - it shuts down the emulator:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -x
set -u

echo -e &quot;auth $(cat ~&#x2F;.emulator_console_auth_token)\nkill&quot; \
    | telnet localhost 5554

echo &quot;Emulator stopped.&quot;
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Submitting a package to F-Droid</title>
          <pubDate>Tue, 17 Jan 2017 21:58:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/01/17/submitting-a-package-to-f-droid/</link>
          <guid>https://artificialworlds.net/blog/2017/01/17/submitting-a-package-to-f-droid/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/01/17/submitting-a-package-to-f-droid/">&lt;p&gt;Here&#x27;s what I needed to get a dev environment for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;f-droid.org&#x2F;&quot;&gt;F-Droid&lt;&#x2F;a&gt; up and running on Ubuntu 16.10, using F-Droid server version 0.7.0 (commit id 8147f9235), so that I could submit a package for inclusion in the F-Droid repository.&lt;&#x2F;p&gt;
&lt;p&gt;Doing this is apparently the best way to get your own package into the repository, since you can provide a direct merge request for the metadata about your package, making it easy for the maintainers.&lt;&#x2F;p&gt;
&lt;p&gt;References:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;f-droid.org&#x2F;manual&#x2F;fdroid.html&quot;&gt;F-Droid manual&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroiddata&#x2F;blob&#x2F;master&#x2F;CONTRIBUTING.md&quot;&gt;F-Droid&#x27;s CONTRIBUTING.md&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroiddata&#x2F;blob&#x2F;master&#x2F;README.md#quickstart&quot;&gt;F-Droid Quickstart&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;f-droid.org&#x2F;contribute&#x2F;&quot;&gt;Contribute to F-Droid&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h2&gt;
&lt;p&gt;Before you start, manually install the Android SDK at ~&#x2F;Android&#x2F;Sdk&#x2F; - see &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;studio&#x2F;index.html#downloads&quot;&gt;Download Android Studio&lt;&#x2F;a&gt;. I installed version 23.0.2, but you will probably have a later one and may need to adjust the version number below.&lt;&#x2F;p&gt;
&lt;p&gt;Note: If you&#x27;re only planning to contribute a package I&#x27;m fairly certain you don&#x27;t need to install the Android SDK at all - you can just use the build server by running .&#x2F;makebuildserver as I outline below.&lt;&#x2F;p&gt;
&lt;p&gt;Also before you start, if you want to contribute to the server project you should fork the F-Droid server project by going to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroidserver&quot;&gt;gitlab.com&#x2F;fdroid&#x2F;fdroidserver&lt;&#x2F;a&gt; and clicking Fork. When you&#x27;ve done that, the git clone command below will need to change to clone your own fork via SSH, instead of the HTTPS one cloning the main repo that is shown below. Do the same for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroiddata&quot;&gt;F-Droid data project&lt;&#x2F;a&gt;, which holds the information about the packages in F-Droid. It&#x27;s the data project where you will want to make changes if you are submitting a package.&lt;&#x2F;p&gt;
&lt;p&gt;Run these commands:&lt;&#x2F;p&gt;
&lt;pre&gt;# Prerequisites
sudo apt-get install openjdk-8-jdk subversion git git-svn mercurial bzr virtualbox ruby ruby-dev vagrant python3 python3-paramiko python3-pil python3-pyasn1-modules python3-clint
vagrant plugin install vagrant-cachier
ln -s ~&#x2F;Android&#x2F;Sdk&#x2F;build-tools&#x2F;23.0.2&#x2F;aapt ~&#x2F;Android&#x2F;Sdk&#x2F;platform-tools&#x2F;

# Get the code
cd ~&#x2F;code
git clone https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroidserver.git
git clone https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroiddata.git
echo &#x27;export PATH=&quot;~&#x2F;code&#x2F;fdroidserver:$PATH&quot;&#x27; &amp;gt;&amp;gt; ~&#x2F;.profile
source ~&#x2F;.profile

# Config
cd fdroiddata
cp ..&#x2F;fdroidserver&#x2F;examples&#x2F;config.py .&#x2F;
chmod 0600 config.py
echo &#x27;sdk_path = &quot;$HOME&#x2F;Android&#x2F;Sdk&quot;&#x27; &amp;gt;&amp;gt; config.py

# Set up Vagrant build box
cd ..&#x2F;fdroidserver
cp .&#x2F;examples&#x2F;makebuildserver.config.py .&#x2F;
.&#x2F;makebuildserver
# Now wait several hours for this to finish

# Build a package (the F-Droid client) just to check it works
cd ..&#x2F;fdroiddata
mkdir repo
fdroid update --create-key
fdroid readmeta  # Should give no output if it worked
fdroid build --server org.fdroid.fdroid
# Again, this could take several hours!
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;make-your-own-package&quot;&gt;Make your own package&lt;&#x2F;h2&gt;
&lt;p&gt;Below I&#x27;m using my own package, Rabbit Escape, as an example. Its Android code is inside rabbit-escape-ui-android&#x2F;app, whereas many programs will just have it directly in a directory called &quot;app&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Rabbit Escape also builds non-Android-specific Java and other things during its build, so your package may be simpler.&lt;&#x2F;p&gt;
&lt;pre&gt;cd ..&#x2F;fdroiddata
fdroid import --url https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape --subdir rabbit-escape-ui-android&#x2F;app
&lt;&#x2F;pre&gt;
&lt;p&gt;Now edit the new file that was created - in my case it was called metadata&#x2F;net.artificialworlds.rabbitescape.txt.&lt;&#x2F;p&gt;
&lt;p&gt;I set the following info:&lt;&#x2F;p&gt;
&lt;pre&gt;Provides:net.artificialworlds.rabbitescape
Categories:Games
License:GPLv2+
Author Name:Andy Balaam and the Rabbit Escape developers
Author Email:rabbitescape@artificialworlds.net
Web Site:http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape
Source Code:https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape
Issue Tracker:https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;issues

Name:Rabbit Escape
Summary:Lemmings-like puzzle&#x2F;action game
Description:
140 levels of puzzling action!

blah blah blah
.

Repo Type:git
Repo:https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape
Binaries:https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;releases&#x2F;download&#x2F;v%v&#x2F;rabbit-escape-%v.apk

Build:0.10.1,101
    commit=v0.10.1
    subdir=rabbit-escape-ui-android&#x2F;app
    gradle=paid
    build=cd ..&#x2F;.. &amp;amp;&amp;amp; make android-pre

Auto Update Mode:Version v%v
Update Check Mode:Tags v\d+\.\d+(\.\d+)?
Current Version:0.10.1
Current Version Code:101
&lt;&#x2F;pre&gt;
&lt;p&gt;For more info, see the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;f-droid.org&#x2F;manual&#x2F;fdroid.html#Categories&quot;&gt;F-Droid manual&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And then checked it all worked with:&lt;&#x2F;p&gt;
&lt;pre&gt;cd ..&#x2F;fdroiddata
fdroid lint net.artificialworlds.rabbitescape
fdroid readmeta
fdroid checkupdates net.artificialworlds.rabbitescape
fdroid rewritemeta net.artificialworlds.rabbitescape
&lt;&#x2F;pre&gt;
&lt;p&gt;When I got the version stuff right the checkupdates command printed:&lt;&#x2F;p&gt;
&lt;pre&gt;INFO: Processing net.artificialworlds.rabbitescape...
INFO: ...updating to version 0.10.1 (101)
INFO: Finished.
&lt;&#x2F;pre&gt;
&lt;p&gt;Then I made sure it built OK:&lt;&#x2F;p&gt;
&lt;pre&gt;fdroid build --server -v -l net.artificialworlds.rabbitescape
&lt;&#x2F;pre&gt;
&lt;p&gt;Actually, it didn&#x27;t work, and I decided I had to request a new package (sox) be installed in the build machine environment (in the fdroidserver project). The relevant commit is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;andybalaam&#x2F;fdroidserver&#x2F;commit&#x2F;19e372026aaca62349e6f5fd541dc3ba29d94836&quot;&gt;19e372026&lt;&#x2F;a&gt;. Actually though, after &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;botbot.me&#x2F;freenode&#x2F;fdroid-dev&#x2F;2017-01-17&#x2F;?msg=79512863&amp;amp;page=1&quot;&gt;discussion with the F-Droid devs&lt;&#x2F;a&gt; we agreed I&#x27;d be better off not using sox during the build, so I didn&#x27;t need this.&lt;&#x2F;p&gt;
&lt;p&gt;Side note: if you do end up needing to modify the build environment for F-Droid, make sure you delete the fdroiddata&#x2F;buildserver directory when you re-try your build. That one had me stuck for a few days, with the old environment being used no matter what caches I cleared and vagrant commands I ran.&lt;&#x2F;p&gt;
&lt;p&gt;And now I was ready to request my package be included in F-Droid by committing and pushing the changes I had made to the fdroiddata project to my forked repo, and clicking the Merge Request button in the gitlab UI. My merge request is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitlab.com&#x2F;fdroid&#x2F;fdroiddata&#x2F;merge_requests&#x2F;1965&quot;&gt;gitlab.com&#x2F;fdroid&#x2F;fdroiddata&#x2F;merge_requests&#x2F;1965&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Kar
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Note that you need a lot of space to build this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mirek
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;How long should the &quot;fdroid build —server org.fdroid.fdroid&quot; step take? It seems like it’s building every possible version of fdroid server in a virtual machine. Is that the desired outcome? Or should I just cancel after a few builds?&lt;&#x2F;p&gt;
&lt;p&gt;Could you clarify this step in the official guide?&lt;&#x2F;p&gt;
&lt;p&gt;Thanks in advance.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;For me it took a &lt;em&gt;very&lt;&#x2F;em&gt; long time (maybe even days!) for some steps, so I’d recommend leaving it and seeing what happens!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I added a note that the last step could also take several hours.  Thanks Mirek!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Resources for year 6 teachers on coding and programming</title>
          <pubDate>Mon, 09 Jan 2017 16:23:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2017/01/09/resources-for-year-6-teachers-on-coding-and-programming/</link>
          <guid>https://artificialworlds.net/blog/2017/01/09/resources-for-year-6-teachers-on-coding-and-programming/</guid>
          <description xml:base="https://artificialworlds.net/blog/2017/01/09/resources-for-year-6-teachers-on-coding-and-programming/">&lt;p&gt;I have been introducing some year 6 (UK) teachers to coding by showing them how to lay out a simple web page by writing HTML. I promised I would find some links to resources for them, so here it is:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;html-and-javascript&quot;&gt;HTML and JavaScript&lt;&#x2F;h2&gt;
&lt;p&gt;My examples of how to write HTML are at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;html-examples&quot;&gt;github.com&#x2F;andybalaam&#x2F;html-examples&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There are several web sites that allow you to experiment with writing HTML and JavaScript and seeing the results immediately:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jsfiddle.net&#x2F;&quot;&gt;jsfiddle&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;repl.it&#x2F;languages&#x2F;web_project&quot;&gt;repl.it&lt;&#x2F;a&gt; - this also has a &quot;classroom&quot; mode here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;repl.it&#x2F;site&#x2F;classrooms&quot;&gt;repl.it&#x2F;site&#x2F;classrooms&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I also made some videos about how to make a snowflake animation in both JavaScript and Scratch here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLgyU3jNA6VjSbjmQRSNoi2IOtydOw9IW-&quot;&gt;Snowflake Christmas card&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;raspberry-pi&quot;&gt;Raspberry Pi&lt;&#x2F;h2&gt;
&lt;p&gt;The Raspberry Pi is a cheap education-focussed computer that looks like a piece of circuit board the size of a credit card.&lt;&#x2F;p&gt;
&lt;p&gt;Their educational resources are at: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;resources&#x2F;&quot;&gt;raspberrypi.org&#x2F;resources&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are lots of great videos about how to do different things with the Raspberry Pi, including the ones by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.theraspberrypiguy.com&#x2F;tutorials&#x2F;&quot;&gt;The Raspberry Pi Guy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are also my (boring, but comprehensive) videos teaching you to write a simple game in Python, from a starting point of no programming experience at all: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLgyU3jNA6VjS3ij6ZXbb2x4GdEP3bAWzO&quot;&gt;My First Raspberry Pi Game&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;graphical-programming&quot;&gt;Graphical programming&lt;&#x2F;h2&gt;
&lt;p&gt;There are several tools and sites for learning programming by dragging and dropping blocks instead of typing code:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;scratch.mit.edu&#x2F;&quot;&gt;Scratch&lt;&#x2F;a&gt; - creative, unguided, a bit old-fashined looking but tried-and-trusted&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;code.org&#x2F;&quot;&gt;code.org&lt;&#x2F;a&gt; - fun, attractive guided tasks featuring Disney characters, Minecraft etc.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blockly-games.appspot.com&#x2F;&quot;&gt;blockly&lt;&#x2F;a&gt; - guided tasks with good progression&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.codeforlife.education&#x2F;&quot;&gt;codeforlife.education&lt;&#x2F;a&gt; - I&#x27;ve not used this, but it looks like it could have potential&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;other-languages&quot;&gt;Other languages&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.helloruby.com&#x2F;&quot;&gt;Hello, Ruby&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.java.com&#x2F;en&#x2F;java_in_action&#x2F;alice.jsp&quot;&gt;Java - Alice&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.apple.com&#x2F;swift&#x2F;playgrounds&#x2F;&quot;&gt;Swift - Playgrounds&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; there is a huge list of similar resources here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vodien.com&#x2F;blog&#x2F;education&#x2F;beginners-resources-to-learn-programming-languages.php&quot;&gt;Beginner&#x27;s Resources to Learn Programming Languages&lt;&#x2F;a&gt;. Thanks to Erica and Sarah for emailing me the link!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 2:&lt;&#x2F;strong&gt; some more useful links: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;couponfollow.com&#x2F;research&#x2F;learn-to-code-from-home&quot;&gt;Learn to code from home&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hp.com&#x2F;us-en&#x2F;shop&#x2F;tech-takes&#x2F;computer-language-for-beginners-html&quot;&gt;Computer Language for beginners: HTML&lt;&#x2F;a&gt;. Thanks to Sarah for the suggestions, and Stacey for sending them to me!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 3:&lt;&#x2F;strong&gt; a comprehensive page of links to resources for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;vr.space&#x2F;news&#x2F;education&#x2F;vr-coding-design&#x2F;&quot;&gt;learning how to code Virtual Reality&lt;&#x2F;a&gt;. Thanks to Katie for the suggestion, and Yazmin for sending it. Keep on being a computer nerd!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Why I won’t link to AI resources – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2024-05-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] received a very kind email today from someone who had found my page Resources for year 6 teachers on coding and programming helpful, and wanted to suggest another link for me to add, about AI […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dana
    &lt;span class=&quot;comment_date&quot;&gt;2025-01-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is such a valuable resource&lt;a href=&quot;https:&#x2F;&#x2F;www.timelycoupons.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;!&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2025-01-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Setting up a sane Maven project</title>
          <pubDate>Fri, 23 Dec 2016 16:07:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/12/23/setting-up-a-sane-maven-project/</link>
          <guid>https://artificialworlds.net/blog/2016/12/23/setting-up-a-sane-maven-project/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/12/23/setting-up-a-sane-maven-project/">&lt;p&gt;Today I have spent hours trying to get wrangle Maven into letting me set up a sane Java project. The hardest parts were enforcing warnings-as-errors, and getting Maven to shut up a bit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;some-code-that-warns&quot;&gt;Some code that warns&lt;&#x2F;h2&gt;
&lt;p&gt;My first problem was writing some Java code that causes the compiler to emit warnings. For some reason I can&#x27;t understand, my Java 1.8 compiler was not emitting warnings (even with -Xlint:all) for unused variables, use of deprecated methods, or unknown @SuppressWarnings types (suggestions from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;1752607&#x2F;how-to-intentionally-cause-a-custom-java-compiler-warning-message&#x2F;41303476#41303476&quot;&gt;SO 1752607&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Instead, I had to use an unnecessary cast:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat src&#x2F;tast&#x2F;java&#x2F;ExampleTest.java
public class ExampleTest {
    public void warn() {
        String fixmePlease = (String)&quot;Hello&quot;;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Now, finally, I got a warning:&lt;&#x2F;p&gt;
&lt;pre&gt;$ javac -Xlint:all src&#x2F;test&#x2F;ExampleTest.java
src&#x2F;test&#x2F;ExampleTest.java:3: warning: [cast] redundant cast to String
        String s = (String) &quot;Hello!&quot;;
                   ^
1 warning&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;maven-compiler-settings-for-warnings-as-errors&quot;&gt;Maven compiler settings for warnings-as-errors&lt;&#x2F;h2&gt;
&lt;p&gt;I tried a vast set of combinations of properties like maven.compiler.failOnWarning and maven.compiler.fork (as specified in the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;plugins&#x2F;maven-compiler-plugin&#x2F;compile-mojo.html&quot;&gt;maven compiler plugin docs&lt;&#x2F;a&gt;) before giving up on properties. Making a property called maven.compiler.failOnWarning seems to have no effect whatsoever, ever.&lt;&#x2F;p&gt;
&lt;p&gt;So I decided I must try the (very verbose) plugin tag containing a configuration tag, as also specified in the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;plugins&#x2F;maven-compiler-plugin&#x2F;compile-mojo.html&quot;&gt;maven compiler plugin docs&lt;&#x2F;a&gt;. After a lot of messing about with flags that seemed incompatible, and Maven silently ignoring things it didn&#x27;t understand, I came to a working config.&lt;&#x2F;p&gt;
&lt;p&gt;On the way, I discovered that setting the &quot;fork&quot; property to true is a non-starter, because Maven simply drops the compiler output in that case, meaning you can&#x27;t see what is going wrong when it does.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I had a pom file like this:&lt;&#x2F;p&gt;
&lt;pre&gt;cat pom.xml
&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;POM&#x2F;4.0.0&quot; xmlns:xsi=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2001&#x2F;XMLSchema-instance&quot; xsi:schemaLocation=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;POM&#x2F;4.0.0 http:&#x2F;&#x2F;maven.apache.org&#x2F;maven-v4_0_0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;&#x2F;modelVersion&amp;gt;
    &amp;lt;groupId&amp;gt;com.example&amp;lt;&#x2F;groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;Example&amp;lt;&#x2F;artifactId&amp;gt;
    &amp;lt;packaging&amp;gt;jar&amp;lt;&#x2F;packaging&amp;gt;
    &amp;lt;version&amp;gt;0.0.1&amp;lt;&#x2F;version&amp;gt;
    &amp;lt;properties&amp;gt;
        &amp;lt;project.build.sourceEncoding&amp;gt;UTF-8&amp;lt;&#x2F;project.build.sourceEncoding&amp;gt;
    &amp;lt;&#x2F;properties&amp;gt;
    &amp;lt;build&amp;gt;
        &amp;lt;plugins&amp;gt;
            &amp;lt;plugin&amp;gt;
                &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;&#x2F;groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;&#x2F;artifactId&amp;gt;
                &amp;lt;version&amp;gt;3.6.0&amp;lt;&#x2F;version&amp;gt;
                &amp;lt;configuration&amp;gt;
                    &amp;lt;source&amp;gt;1.8&amp;lt;&#x2F;source&amp;gt;
                    &amp;lt;target&amp;gt;1.8&amp;lt;&#x2F;target&amp;gt;
                    &amp;lt;failOnWarning&amp;gt;true&amp;lt;&#x2F;failOnWarning&amp;gt;
                    &amp;lt;showWarnings&amp;gt;true&amp;lt;&#x2F;showWarnings&amp;gt;
                    &amp;lt;compilerArgs&amp;gt;
                        &amp;lt;arg&amp;gt;-Xlint:all&amp;lt;&#x2F;arg&amp;gt;
                    &amp;lt;&#x2F;compilerArgs&amp;gt;
                &amp;lt;&#x2F;configuration&amp;gt;
            &amp;lt;&#x2F;plugin&amp;gt;
        &amp;lt;&#x2F;plugins&amp;gt;
    &amp;lt;&#x2F;build&amp;gt;
     &amp;lt;dependencies&amp;gt;
         &amp;lt;dependency&amp;gt;
             &amp;lt;groupId&amp;gt;junit&amp;lt;&#x2F;groupId&amp;gt;
             &amp;lt;artifactId&amp;gt;junit&amp;lt;&#x2F;artifactId&amp;gt;
             &amp;lt;version&amp;gt;4.12&amp;lt;&#x2F;version&amp;gt;
             &amp;lt;scope&amp;gt;test&amp;lt;&#x2F;scope&amp;gt;
         &amp;lt;&#x2F;dependency&amp;gt;
     &amp;lt;&#x2F;dependencies&amp;gt;
&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Which manages successfully to tell the compiler to show all warnings, and to fail when it sees one. (Thanks to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;9192613&#x2F;how-do-i-get-a-java-maven-build-to-fail-for-compiler-warnings&quot;&gt;SO 9192613&lt;&#x2F;a&gt;, among others.)&lt;&#x2F;p&gt;
&lt;p&gt;I dare not change anything, for fear that it will stop working again without any explanation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;quieting-maven&quot;&gt;Quieting Maven&lt;&#x2F;h2&gt;
&lt;p&gt;If you tell Maven to be quiet with -q or -e it will merrily fail the build because of a warning, but not tell you what the warning was.&lt;&#x2F;p&gt;
&lt;p&gt;Maven does not appear to have a command line option to set the log level to display warnings and errors only, but you can force it to do so by setting the environment variable MAVEN_OPTS like this:&lt;&#x2F;p&gt;
&lt;pre&gt;MAVEN_OPTS=MAVEN_OPTS=-Dorg.slf4j.simpleLogger.defaultLogLevel=warn mvn clean test-compile&lt;&#x2F;pre&gt;
&lt;p&gt;(Thanks to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;4782089&#x2F;how-to-change-maven-logging-level-to-display-only-warning-and-errors&quot;&gt;SO 4782089&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;And, with some guesswork (partly based on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;maven.apache.org&#x2F;configure.html&quot;&gt;Configuring Maven&lt;&#x2F;a&gt;) I found that if I put something similar in .mvn&#x2F;jvm.config I didn&#x27;t have to type it every time:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat .mvn&#x2F;jvm.config
-Dorg.slf4j.simpleLogger.defaultLogLevel=warn
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;failing-on-warnings-and-seeing-them&quot;&gt;Failing on warnings, and seeing them!&lt;&#x2F;h2&gt;
&lt;p&gt;I don&#x27;t know whether to feel triumphant or defeated, but, it works!&lt;&#x2F;p&gt;
&lt;pre&gt;$ mvn clean test-compile
[WARNING] COMPILATION WARNING :
[WARNING] src&#x2F;test&#x2F;java&#x2F;ExampleTest.java:[3,20] redundant cast to java.lang.String
[ERROR] COMPILATION ERROR :
[ERROR] src&#x2F;test&#x2F;java&#x2F;ExampleTest.java: warnings found and -Werror specified
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.6.0:testCompile (default-testCompile) on project unmatcheddrrepair: Compilation failure
[ERROR] src&#x2F;test&#x2F;java&#x2F;ExampleTest.java: warnings found and -Werror specified
[ERROR] -&amp;gt; [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http:&#x2F;&#x2F;cwiki.apache.org&#x2F;confluence&#x2F;display&#x2F;MAVEN&#x2F;MojoFailureException&lt;&#x2F;pre&gt;
&lt;p&gt;I wish I could suppress double-reporting of the error, and the extra rubbish at the end (and the super-long absolute paths of each file that push the actual errors off the side of the screen), but at this point, I must try and do what I was trying to do in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;Guess how much I am liking Maven at this point.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Writing a unit test in Elm</title>
          <pubDate>Tue, 01 Nov 2016 22:05:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/11/01/writing-a-unit-test-in-elm/</link>
          <guid>https://artificialworlds.net/blog/2016/11/01/writing-a-unit-test-in-elm/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/11/01/writing-a-unit-test-in-elm/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Snake in Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;elm-makes-me-happy-updated-for-elm-0-19-video&#x2F;&quot;&gt;Elm makes me happy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;05&#x2F;13&#x2F;elm-basics-video&#x2F;&quot;&gt;Elm Basics&lt;&#x2F;a&gt;, Elm Unit Test, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;10&#x2F;19&#x2F;elm-json-decoder-examples&#x2F;&quot;&gt;Elm JSON&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been having &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;sootl&#x2F;&quot;&gt;fun&lt;&#x2F;a&gt; with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; programming recently. Elm is a replacement for JavaScript that is pure functional and highly practical.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how to go from nothing installed at all to writing a unit test that passes, in just over 10 minutes.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Writing a unit test in Elm&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;782f8834-22bf-45ec-9980-a1dd7421aa20&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;The source code is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;elm-unit-test-example&quot;&gt;github.com&#x2F;andybalaam&#x2F;elm-unit-test-example&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Snake in Elm – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-11-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Writing a unit test in Elm […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Elm Basics Video – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2018-11-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test, Elm […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Elm makes me happy (updated for Elm 0.19) video – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2018-11-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit Test, Elm […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>How to write a programming language - Part 3, The Evaluator</title>
          <pubDate>Sun, 16 Oct 2016 23:18:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/10/16/how-to-write-a-programming-language-part-3-the-evaluator/</link>
          <guid>https://artificialworlds.net/blog/2016/10/16/how-to-write-a-programming-language-part-3-the-evaluator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/10/16/how-to-write-a-programming-language-part-3-the-evaluator/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;09&#x2F;24&#x2F;how-to-write-a-programming-language-part-1-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;10&#x2F;09&#x2F;how-to-write-a-programming-language-part-2-the-parser&#x2F;&quot;&gt;Parser&lt;&#x2F;a&gt;, Evaluator.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we get onto the actual magic of the little language I wrote (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&quot;&gt;Cell&lt;&#x2F;a&gt;) - the evaluator, which takes in syntax trees and finds their real value, in the context of the &quot;environment&quot;: the symbols that are defined around it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;3. Writing a programming language - the Evaluator&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;cb363acc-e481-4198-bf35-48d10effff3f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;writing-cell-evaluator&#x2F;writing-cell-evaluator.html&quot;&gt;How to write a programming language - Part 3, The Evaluator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Basic Haskell project setup (unit tests, code, formatting)</title>
          <pubDate>Fri, 14 Oct 2016 01:56:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/10/14/basic-haskell-project-setup-unit-tests-code-formatting/</link>
          <guid>https://artificialworlds.net/blog/2016/10/14/basic-haskell-project-setup-unit-tests-code-formatting/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/10/14/basic-haskell-project-setup-unit-tests-code-formatting/">&lt;p&gt;To start a programming project, we need to be able to build, format code, and run unit tests.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I have found makes a sensible starting point for a Haskell project. Full code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;hunit-example&quot;&gt;hunit-example&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To build and run tests, just do:&lt;pre&gt;make setup
make test&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I tend to use a Makefile for all the commands I need to remember. The most important part here is the &quot;setup&quot; target which installs the cabal-install package (&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.haskell.org&#x2F;cabal&#x2F;&quot;&gt;Cabal&lt;&#x2F;a&gt; is a package manager for Haskell), then uses Cabal to install &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chrisdone&#x2F;hindent&quot;&gt;hindent&lt;&#x2F;a&gt;, and finally installs all the dependencies of our actual project (which we specify in a .cabal file, shown further down). The format target uses hindent-all, which is shown below.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: added &quot;happy&quot; as a prerequisite - needed by hindent.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Makefile:&lt;pre&gt;all: test&lt;&#x2F;p&gt;
&lt;p&gt;test: format
cabal test&lt;&#x2F;p&gt;
&lt;p&gt;build: format
cabal build&lt;&#x2F;p&gt;
&lt;p&gt;format:
.&#x2F;hindent-all&lt;&#x2F;p&gt;
&lt;p&gt;clean:
cabal clean&lt;&#x2F;p&gt;
&lt;p&gt;setup:
sudo apt-get install cabal-install happy
cabal update
cabal install hindent
cabal install --run-tests&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Our production code is very simple - just two functions.&lt;&#x2F;p&gt;
&lt;p&gt;HUnitExample.hs:&lt;pre&gt;module HUnitExample where&lt;&#x2F;p&gt;
&lt;p&gt;double x = x * 2&lt;&#x2F;p&gt;
&lt;p&gt;half x = x &#x2F; 2&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Our tests import our production code, and use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;tasty&quot;&gt;tasty&lt;&#x2F;a&gt; (which includes &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;HUnit&quot;&gt;HUnit&lt;&#x2F;a&gt;) to check it works.&lt;&#x2F;p&gt;
&lt;p&gt;test&#x2F;Tests.hs:&lt;pre&gt;import HUnitExample (double, half)
import Test.Tasty (defaultMain, testGroup)
import Test.Tasty.HUnit (assertEqual, testCase)&lt;&#x2F;p&gt;
&lt;p&gt;main = defaultMain unitTests&lt;&#x2F;p&gt;
&lt;p&gt;unitTests =
testGroup
&quot;Unit tests&quot;
[doublingMakesNumbersBigger, halvingMakesNumbersSmaller]&lt;&#x2F;p&gt;
&lt;p&gt;doublingMakesNumbersBigger =
testCase &quot;Double of 4 is 8&quot; $ assertEqual [] 8 (double 4)&lt;&#x2F;p&gt;
&lt;p&gt;halvingMakesNumbersSmaller =
testCase &quot;Half of 9 is 4&quot; $ assertEqual [] 4 (half 9)&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Each test is a testCase that uses assertions like assertEqual to verify the code. HUnit also provides lots of cryptic operators for obfuscating your tests.&lt;&#x2F;p&gt;
&lt;p&gt;Many projects will want to add property-based testing with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;QuickCheck&quot;&gt;QuickCheck&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;leepike&#x2F;SmartCheck&quot;&gt;SmartCheck&lt;&#x2F;a&gt; or similar.&lt;&#x2F;p&gt;
&lt;p&gt;The magic that makes all this work is the Cabal file, which can have any name that ends in &quot;.cabal&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;hunit-example.cabal:&lt;pre&gt;Name:               hunit-example
Version:            1.0.0
cabal-version:      &amp;gt;= 1.8
build-type:         Simple&lt;&#x2F;p&gt;
&lt;p&gt;Library
Exposed-Modules:  HUnitExample
Build-Depends:    base &amp;gt;= 3 &amp;amp;&amp;amp; &amp;lt; 5&lt;&#x2F;p&gt;
&lt;p&gt;Test-Suite test-hunit-example
type:             exitcode-stdio-1.0
hs-source-dirs:   tests
Main-is:          Tests.hs
Build-Depends:    base &amp;gt;= 3 &amp;amp;&amp;amp; &amp;lt; 5
, tasty
, tasty-hunit
, hunit-example&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You will normally want lots more properties than this, but this is what I think is a minimal example. It defines the project properties, the production library module we are building, and a test suite that tasty uses to know what to run. Note that the test suite must depend on the production code module so you are allowed to import it.&lt;&#x2F;p&gt;
&lt;p&gt;To do code formatting, I made this bash script:&lt;&#x2F;p&gt;
&lt;p&gt;hindent-all:&lt;pre&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;h1 id=&quot;format-all-hs-files-in-the-current-directory-tree-with-hindent&quot;&gt;Format all .hs files in the current directory tree with hindent&lt;&#x2F;h1&gt;
&lt;p&gt;HINDENT=${HOME}&#x2F;.cabal&#x2F;bin&#x2F;hindent&lt;&#x2F;p&gt;
&lt;p&gt;function all_hs_files()
{
find .&#x2F; -name dist -prune -o -name &quot;*.hs&quot; -print
}&lt;&#x2F;p&gt;
&lt;p&gt;for FILE in $(all_hs_files); do
{
NEWFILE=${FILE}.formatted
cat ${FILE} | ${HINDENT} &amp;gt; ${NEWFILE}&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;if diff -q ${FILE} ${NEWFILE} &amp;amp;gt;&#x2F;dev&#x2F;null; then
{
    rm ${NEWFILE}
}
else
{
    echo &amp;quot;hindent updated ${FILE}&amp;quot;
    mv ${NEWFILE} ${FILE}
}; fi
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}; done&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I would be grateful if someone pointed out I didn&#x27;t need to do that because someone has done it for me. Note: I chose not to use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;danstiner&#x2F;hfmt&quot;&gt;hfmt&lt;&#x2F;a&gt; because it hard-codes a long line length, whereas hindent behaves how I like with no configuration.&lt;&#x2F;p&gt;
&lt;p&gt;Last but not least, ignore the directory created by Cabal.&lt;&#x2F;p&gt;
&lt;p&gt;.gitignore:&lt;pre&gt;dist&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So, when we run we see this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ make test
...
Running 1 test suites...
Test suite test-hunit-example: RUNNING...
Unit tests
  Double of 4 is 8: OK
  Half of 9 is 4:   FAIL
    expected: 4.0
     but got: 4.5

1 out of 2 tests failed (0.00s)
...&lt;&#x2F;pre&gt;
&lt;p&gt;Looks like we&#x27;ve got a bug to fix...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to write a programming language - Part 2, The Parser</title>
          <pubDate>Sun, 09 Oct 2016 23:47:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/10/09/how-to-write-a-programming-language-part-2-the-parser/</link>
          <guid>https://artificialworlds.net/blog/2016/10/09/how-to-write-a-programming-language-part-2-the-parser/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/10/09/how-to-write-a-programming-language-part-2-the-parser/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;09&#x2F;24&#x2F;how-to-write-a-programming-language-part-1-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt;, Parser, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;10&#x2F;16&#x2F;how-to-write-a-programming-language-part-3-the-evaluator&#x2F;&quot;&gt;Evaluator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;My little programming language, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&quot;&gt;Cell&lt;&#x2F;a&gt; (Cell Elementary Learning Language) is designed to be simple. I want to use it to explain how to write a programming language. The parser is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&#x2F;blob&#x2F;master&#x2F;pycell&#x2F;parser.py&quot;&gt;only 81 lines long&lt;&#x2F;a&gt;, so hopefully it&#x27;s not too hard to understand.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the explanation of the parser, which is the second part of a compiler or interpreter.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;2. Writing a programming language - the Parser&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;f3ec9e1e-81c4-43e9-bfaf-3ac4298c45c9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;writing-cell-parser&#x2F;writing-cell-parser.html&quot;&gt;How to write a programming language - Part 2, The Parser&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    How to write a programming language – Part 1, The Lexer – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-10-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] How to write a programming language – Part 2, The Parser […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Mousedown events delayed or sluggish on mobile</title>
          <pubDate>Fri, 30 Sep 2016 20:47:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/09/30/mousedown-events-delayed-or-sluggish-on-mobile/</link>
          <guid>https://artificialworlds.net/blog/2016/09/30/mousedown-events-delayed-or-sluggish-on-mobile/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/09/30/mousedown-events-delayed-or-sluggish-on-mobile/">&lt;p&gt;I am learning about &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; and web apps by writing a little game called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;sootl&quot;&gt;sootl&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s all SVGs animated with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;API&#x2F;window&#x2F;requestAnimationFrame&quot;&gt;requestAnimationFrame()&lt;&#x2F;a&gt;, and you control your player by clicking or touch the screen.&lt;&#x2F;p&gt;
&lt;p&gt;Everything seemed good on desktop browsers, but on mobile it took an age for the mousedown event to trigger when you touched.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mastodon.social&#x2F;users&#x2F;zatnosk&quot;&gt;@zatnosk on mastodon.social&lt;&#x2F;a&gt; for letting me know there is a better way: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;web&#x2F;updates&#x2F;2013&#x2F;12&#x2F;300ms-tap-delay-gone-away&quot;&gt;300ms tap delay, gone away&lt;&#x2F;a&gt;. Short answer: add a &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width&quot;&amp;gt; tag inside your &amp;lt;head&amp;gt; tag.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.html5rocks.com&#x2F;en&#x2F;mobile&#x2F;touchandmouse&#x2F;&quot;&gt;It turns out&lt;&#x2F;a&gt; that under certain circumstances, some mobile devices wait after the first touch happens to see whether you are going to swipe before they emit the mousedown event.&lt;&#x2F;p&gt;
&lt;p&gt;So, the short answer was to listen for touchstart events as well as mousedown ones. In Elm that looked like this change: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;sootl&#x2F;commit&#x2F;e77e055e470c38489657ecaf5edc54a4e5f85782&quot;&gt;e77e055e470c38489657ecaf5edc54a4e5f85782&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to write a programming language - Part 1, The Lexer</title>
          <pubDate>Sat, 24 Sep 2016 07:39:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/09/24/how-to-write-a-programming-language-part-1-the-lexer/</link>
          <guid>https://artificialworlds.net/blog/2016/09/24/how-to-write-a-programming-language-part-1-the-lexer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/09/24/how-to-write-a-programming-language-part-1-the-lexer/">&lt;p&gt;Series: Lexer, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;10&#x2F;09&#x2F;how-to-write-a-programming-language-part-2-the-parser&#x2F;&quot;&gt;Parser&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;10&#x2F;16&#x2F;how-to-write-a-programming-language-part-3-the-evaluator&#x2F;&quot;&gt;Evaluator&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I wrote a little programming language, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;cell&quot;&gt;Cell&lt;&#x2F;a&gt; which is supposed to be simple enough to help explain how a programming language works.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the explanation of the lexer, which is the first part of a compiler or interpreter.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;1. Writing a programming language - the Lexer&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;930a9cbe-0c55-4fc4-aba7-038a0edc9e09&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;writing-cell-lexer&#x2F;writing-cell-lexer.html&quot;&gt;How to write a programming language - Part 1, The Lexer&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Neto chidozie
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You’re such a great teacher.&lt;br&#x2F;&gt;
Nice one, please do more videos on compilers&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Simple example of Netty 4 usage</title>
          <pubDate>Thu, 15 Sep 2016 15:56:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/09/15/simple-example-of-netty-4-usage/</link>
          <guid>https://artificialworlds.net/blog/2016/09/15/simple-example-of-netty-4-usage/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/09/15/simple-example-of-netty-4-usage/">&lt;p&gt;I feel the title of this post over-promises, since I was not able to make an example that seemed simple to me.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, here is a near-minimal example of how to use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;netty.io&quot;&gt;Netty&lt;&#x2F;a&gt; to make a server that shouts back at you whatever you say:&lt;&#x2F;p&gt;
&lt;p&gt;NettyExample.java:&lt;pre&gt;import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import java.nio.charset.Charset;&lt;&#x2F;p&gt;
&lt;p&gt;class NettyExample
{
public static void main( String[] args ) throws Exception
{
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{
&lt;span style=&quot;color:red;&quot;&gt;new ServerBootstrap()&lt;&#x2F;span&gt;
.group( bossGroup, workerGroup )
.channel( NioServerSocketChannel.class )
.childHandler( new Init() )
&lt;span style=&quot;color:red;&quot;&gt;.bind( 1337 )&lt;&#x2F;span&gt;.sync().channel().closeFuture().sync();
}
finally
{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;private static class Init extends ChannelInitializer&amp;lt;socketchannel&amp;gt;
{
    @Override
    public void
    initChannel( SocketChannel ch ) throws Exception
    {
        ch.pipeline().addLast( new ShoutyHandler() );
    }
}

private static class ShoutyHandler extends ChannelInboundHandlerAdapter
{
    @Override
    public void channelRead( ChannelHandlerContext ctx, Object msg )
    {
        try
        {
            Charset utf8 = CharsetUtil.UTF_8;
            String in = ( (ByteBuf)msg ).toString( utf8 );
            &amp;lt;span style=&amp;quot;color:red;&amp;quot;&amp;gt;String out = in.toUpperCase(); &#x2F;&#x2F; Shout!&amp;lt;&#x2F;span&amp;gt;
            ctx.writeAndFlush( Unpooled.copiedBuffer( out, utf8 ) );
        }
        finally
        {
            ReferenceCountUtil.release( msg );
        }
    }

    @Override
    public void exceptionCaught(
        ChannelHandlerContext ctx, Throwable cause )
    {
        cause.printStackTrace();
        ctx.close();
    }
}
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}&lt;&#x2F;socketchannel&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The lines that actually do something useful are highlighted in red. If anyone knows how to make it shorter, please comment below. It seems a lot to me.&lt;&#x2F;p&gt;
&lt;p&gt;To run this, do:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install openjdk-8-jdk
wget &#x27;http:&#x2F;&#x2F;search.maven.org&#x2F;remotecontent?filepath=io&#x2F;netty&#x2F;netty-all&#x2F;4.1.5.Final&#x2F;netty-all-4.1.5.Final.jar -O netty-all-4.1.5.Final.jar&#x27;
javac -Werror -cp netty-all-4.1.5.Final.jar:. NettyExample.java &amp;amp;&amp;amp; java -cp netty-all-4.1.5.Final.jar:. NettyExample&lt;&#x2F;pre&gt;
&lt;p&gt;Then in another terminal:&lt;&#x2F;p&gt;
&lt;pre&gt;echo &quot;Hello, world&quot; | nc localhost 1337&lt;&#x2F;pre&gt;
&lt;p&gt;and observe the response:&lt;&#x2F;p&gt;
&lt;pre&gt;HELLO, WORLD&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;comparison-with-node-js&quot;&gt;Comparison with Node.js&lt;&#x2F;h2&gt;
&lt;p&gt;Just for comparison, here is an approximate equivalent in Node.js:&lt;&#x2F;p&gt;
&lt;p&gt;shouty.js:&lt;pre&gt;var net = require(&#x27;net&#x27;);&lt;&#x2F;p&gt;
&lt;p&gt;var server = &lt;span style=&quot;color:red;&quot;&gt;net.createServer&lt;&#x2F;span&gt;(
function( socket ) {
socket.setEncoding(&#x27;utf8&#x27;);
socket.on(
&#x27;data&#x27;,
function( data ) {
socket.end( &lt;span style=&quot;color:red;&quot;&gt;data.toUpperCase()&lt;&#x2F;span&gt; );
}
)
}
);&lt;&#x2F;p&gt;
&lt;p&gt;&lt;span style=&quot;color:red;&quot;&gt;server.listen( 1337, &quot;localhost&quot; );&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To run it, do:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install nodejs-legacy
node shouty.js&lt;&#x2F;pre&gt;
&lt;p&gt;Then in another terminal:&lt;&#x2F;p&gt;
&lt;pre&gt;echo &quot;Hello, world&quot; | nc localhost 1337&lt;&#x2F;pre&gt;
&lt;p&gt;and observe the response:&lt;&#x2F;p&gt;
&lt;pre&gt;HELLO, WORLD&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Elm resizeable SVG canvas filling the screen</title>
          <pubDate>Fri, 26 Aug 2016 02:10:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/08/26/elm-resizeable-svg-canvas-filling-the-screen/</link>
          <guid>https://artificialworlds.net/blog/2016/08/26/elm-resizeable-svg-canvas-filling-the-screen/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/08/26/elm-resizeable-svg-canvas-filling-the-screen/">&lt;p&gt;I am toying with writing an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;sootl&quot;&gt;SVG-based game&lt;&#x2F;a&gt; in (exciting-looking JavaScript-replacement) &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, and I wanted an SVG that filled the whole screen and resized when the screen resized. I found it harder than I expected, so here is what I came up with for your information and comment.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;elm&#x2F;resizeable-svg&#x2F;&quot;&gt;Try the demo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Because I was using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;package.elm-lang.org&#x2F;packages&#x2F;elm-lang&#x2F;html&#x2F;1.1.0&#x2F;Html-App#programWithFlags&quot;&gt;Html.App.programWithFlags&lt;&#x2F;a&gt; I was not able to shortcut the process and use just elm-reactor - I needed to create an HTML file and compile my code with elm-make.&lt;&#x2F;p&gt;
&lt;p&gt;index.html sets up a full-screen app and passes in the window size:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;!DOCTYPE HTML&amp;gt;
&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&#x2F;&amp;gt;
    &amp;lt;title&amp;gt;Sootl&amp;lt;&#x2F;title&amp;gt;
    &amp;lt;script src=&quot;sootl.js&quot;&amp;gt;&amp;lt;&#x2F;script&amp;gt;
    &amp;lt;style&amp;gt;
        html, body, svg
        {
            margin: 0px;
            padding: 0px;
            border: 0px;
            overflow: hidden;
        }
    &amp;lt;&#x2F;style&amp;gt;
&amp;lt;&#x2F;head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;&#x2F;body&amp;gt;
&amp;lt;script&amp;gt;
var app = Elm.Main.fullscreen(
    {
        width:  window.innerWidth,
        height: window.innerHeight
    }
);
&amp;lt;&#x2F;script&amp;gt;
&amp;lt;&#x2F;html&amp;gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;elm&#x2F;resizeable-svg&#x2F;elm-package.json&quot;&gt;elm-package.json&lt;&#x2F;a&gt; requires the Html, Svg and Window packages:&lt;&#x2F;p&gt;
&lt;pre&gt;{
    &quot;version&quot;: &quot;1.0.0&quot;,
    &quot;summary&quot;: &quot;Stay out of the light!&quot;,
    &quot;repository&quot;: &quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;sootl.git&quot;,
    &quot;license&quot;: &quot;GPL2&quot;,
    &quot;source-directories&quot;: [
        &quot;.&quot;
    ],
    &quot;exposed-modules&quot;: [],
    &quot;dependencies&quot;: {
        &quot;elm-lang&#x2F;core&quot;: &quot;4.0.5 &amp;lt;= v &amp;lt; 5.0.0&quot;,
        &quot;elm-lang&#x2F;html&quot;: &quot;1.1.0 &amp;lt;= v &amp;lt; 2.0.0&quot;,
        &quot;elm-lang&#x2F;svg&quot;: &quot;1.1.1 &amp;lt;= v &amp;lt; 2.0.0&quot;,
        &quot;elm-lang&#x2F;window&quot;: &quot;1.0.0 &amp;lt;= v &amp;lt; 2.0.0&quot;
    },
    &quot;elm-version&quot;: &quot;0.17.1 &amp;lt;= v &amp;lt; 0.18.0&quot;
}&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;elm&#x2F;resizeable-svg&#x2F;Main.elm&quot;&gt;Main.elm&lt;&#x2F;a&gt; contains the Elm code, which starts off with the window size from JavaScript, and then listens to resize events using the Window module.&lt;&#x2F;p&gt;
&lt;pre&gt;import Html exposing (Html)
import Html.App exposing (programWithFlags)
import Svg exposing (..)
import Svg.Attributes exposing (..)
import Window

type alias Flags =
    { width : Int
    , height : Int
    }

type Msg = Resize Int Int

type alias Model =
    { screen :
        { width : Int
        , height : Int
        }
    }

init : Flags -&amp;gt; (Model, Cmd Msg)
init flags =
    (
        { screen =
            { width = flags.width
            , height = flags.height
            }
        }
    , Cmd.none
    )

view : Model -&amp;gt; Html Msg
view model =
    let
        sw = model.screen.width  - 0
        sh = model.screen.height - 0
    in
        svg
        [ width  &amp;lt;| toString sw
        , height &amp;lt;| toString sh
        ]
        [ rect
            [ x &quot;0&quot;
            , y &quot;0&quot;
            , width (toString model.screen.width)
            , height (toString model.screen.height)
            , fill &quot;#eeffee&quot;
            ]
            []
        , text&#x27;
            [ x &amp;lt;| toString &amp;lt;| sw &#x2F; 2
            , y &amp;lt;| toString &amp;lt;| sh &#x2F; 2
            , fontSize &amp;lt;| toString &amp;lt;| sh &#x2F; 10
            , textAnchor &quot;middle&quot;
            ]
            [ text
                ((toString model.screen.width)
                ++ &quot;, &quot;
                ++ (toString model.screen.height))
            ]
        ]

update : Msg -&amp;gt; Model -&amp;gt; (Model, Cmd Msg)
update msg model =
    let m =
        case msg of
            Resize w h -&amp;gt; {model | screen = {width = w, height = h}}
    in
        (m, Cmd.none)

subscriptions : Model -&amp;gt; Sub Msg
subscriptions model =
    Window.resizes (\size -&amp;gt; Resize size.width size.height)

main =
   programWithFlags
     { init = init
     , view = view
     , update = update
     , subscriptions = subscriptions
     }&lt;&#x2F;pre&gt;
&lt;p&gt;I installed all the packages with:&lt;&#x2F;p&gt;
&lt;pre&gt;elm-package install&lt;&#x2F;pre&gt;
&lt;p&gt;Then I compiled the code with:&lt;&#x2F;p&gt;
&lt;pre&gt;elm-make --output=sootl.js Main.elm&lt;&#x2F;pre&gt;
&lt;p&gt;Now I launched elm-reactor:&lt;&#x2F;p&gt;
&lt;pre&gt;elm-reactor&lt;&#x2F;pre&gt;
&lt;p&gt;And navigated my browser to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;localhost:8000&#x2F;index.html&quot;&gt;http:&#x2F;&#x2F;localhost:8000&#x2F;index.html&lt;&#x2F;a&gt; to see it working.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Ambiguous names in Java due to non-normalised unicode - but all OK in Python</title>
          <pubDate>Thu, 11 Aug 2016 08:47:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/08/11/ambiguous-names-in-java-due-to-non-normalised-unicode-but-all-ok-in-python/</link>
          <guid>https://artificialworlds.net/blog/2016/08/11/ambiguous-names-in-java-due-to-non-normalised-unicode-but-all-ok-in-python/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/08/11/ambiguous-names-in-java-due-to-non-normalised-unicode-but-all-ok-in-python/">&lt;p&gt;In Java and several other languages, identifiers (e.g. method names) are allowed to contain unicode characters.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, some combinations of unicode characters are logically identical. For example, Ã¡ (one character: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;unicode-table.com&#x2F;en&#x2F;00E1&#x2F;&quot;&gt;Latin Small Letter a with Acute U+00E1&lt;&#x2F;a&gt;) is the same as aÌ (two characters: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;unicode-table.com&#x2F;en&#x2F;0061&#x2F;&quot;&gt;Latin Small Letter A U+0061&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;unicode-table.com&#x2F;en&#x2F;0301&#x2F;&quot;&gt;Non-spacing Acute Accent U+0301&lt;&#x2F;a&gt;). These combinations are not just similar - they are identical by definition.&lt;&#x2F;p&gt;
&lt;p&gt;Java does not do any &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unicode_equivalence&quot;&gt;normalisation&lt;&#x2F;a&gt; on your code before compiling it, so two identifiers containing equivalent but different unicode combinations are considered different (ref: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;specs&#x2F;jls&#x2F;se7&#x2F;html&#x2F;jls-3.html#jls-3.8&quot;&gt;JLS 7 section 3.8&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat U.java
public class U {
    static String \u00e1() { return &quot;A WITH ACUTE&quot;; }
    static String a\u0301() { return &quot;A + NON-SPACING ACUTE&quot;; }
    public static void main(String[] a) {
        System.out.println(Ã¡());
        System.out.println(aÌ());
    }
}
$ javac U.java &amp;amp;&amp;amp; java U
A WITH ACUTE
A + NON-SPACING ACUTE
&lt;&#x2F;pre&gt;
&lt;p&gt;We can define and use two functions called Ã¡ and aÌ and they are totally independent entities.&lt;&#x2F;p&gt;
&lt;p&gt;But don&#x27;t do this.&lt;&#x2F;p&gt;
&lt;p&gt;Python 3 also allows unicode characters in identifiers, but it avoids the above problem by normalising them (ref: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;reference&#x2F;lexical_analysis.html#identifiers&quot;&gt;Python 3 Reference, section 2.3&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat U.py
#!&#x2F;usr&#x2F;bin&#x2F;env python3

def Ã¡():
    print(&quot;A WITH ACUTE&quot;)

def aÌ():
    print(&quot;A + NON-SPACING ACUTE&quot;)

Ã¡()
aÌ()

$ hexdump -C U.py
23 21 2f 75 73 72 2f 62  69 6e 2f 65 6e 76 20 70  |#!&#x2F;usr&#x2F;bin&#x2F;env p|
79 74 68 6f 6e 33 0a 0a  64 65 66 20 &lt;span style=&quot;color:red;&quot;&gt;c3 a1&lt;&#x2F;span&gt; 28 29  |ython3..def &lt;span style=&quot;color:red;&quot;&gt;..&lt;&#x2F;span&gt;()|
3a 0a 20 20 20 20 70 72  69 6e 74 28 22 41 20 57  |:.    print(&quot;A W|
49 54 48 20 41 43 55 54  45 22 29 0a 0a 64 65 66  |ITH ACUTE&quot;)..def|
20 &lt;span style=&quot;color: blue;&quot;&gt;61 cc 81&lt;&#x2F;span&gt; 28 29 3a 0a  20 20 20 20 70 72 69 6e  | &lt;span style=&quot;color: blue;&quot;&gt;a..&lt;&#x2F;span&gt;():.    prin|
74 28 22 41 20 2b 20 4e  4f 4e 2d 53 50 41 43 49  |t(&quot;A + NON-SPACI|
4e 47 20 41 43 55 54 45  22 29 0a 0a &lt;span style=&quot;color:red;&quot;&gt;c3 a1&lt;&#x2F;span&gt; 28 29  |NG ACUTE&quot;)..&lt;span style=&quot;color:red;&quot;&gt;..&lt;&#x2F;span&gt;()|
0a &lt;span style=&quot;color: blue;&quot;&gt;61 cc 81&lt;&#x2F;span&gt; 28 29 0a 0a                           |.&lt;span style=&quot;color: blue;&quot;&gt;a..&lt;&#x2F;span&gt;()..|
$ .&#x2F;U.py
A + NON-SPACING ACUTE
A + NON-SPACING ACUTE
&lt;&#x2F;pre&gt;
&lt;p&gt;(Legend: A WITH ACUTE, A + NON-SPACING ACUTE) The second definition overwrites the first because they are considered identical. You can call it via either way of saying its name. Both ways of working are scary, but I&#x27;d definitely choose the Python 3 way if I had to.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gracefully shutting down Firefox, to avoid the crash&#x2F;session dialog</title>
          <pubDate>Thu, 16 Jun 2016 12:47:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/06/16/gracefully-shutting-down-firefox-to-avoid-the-crashsession-dialog/</link>
          <guid>https://artificialworlds.net/blog/2016/06/16/gracefully-shutting-down-firefox-to-avoid-the-crashsession-dialog/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/06/16/gracefully-shutting-down-firefox-to-avoid-the-crashsession-dialog/">&lt;p&gt;I normally have several Firefox profiles open, and when I log out without closing the Firefox windows I get the &quot;session restore&quot; dialog on my next login.&lt;&#x2F;p&gt;
&lt;p&gt;This is because of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=336193&quot;&gt;Bug 336193&lt;&#x2F;a&gt; which says that Firefox should shut down gracefully when it receives a SIGTERM signal, which is what happens when I log out (in Ubuntu MATE anyway). At the moment, it shuts down ungracefully, meaning it is treated as if Firefox crashed when it restarts.&lt;&#x2F;p&gt;
&lt;p&gt;So, instead of my normal shutdown button, I have one that launches a script that closes all my Firefoxes first, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

# Prerequisites:
# sudo apt-get install sysvinit-utils xdotool

set -x
set -u

function close_firefoxes()
{
    local PID
    local WID

    PID=$(pidof -s firefox)
    FOUND=$?

    while [ $FOUND ]; do
    {
        WID=$(xdotool search --pid &quot;$PID&quot; | tail -1)
        xdotool windowactivate --sync &quot;$WID&quot;
        xdotool key --clearmodifiers &#x27;ctrl+q&#x27;
        sleep 1
        PID=$(pidof firefox)
        FOUND=$?
    }; done
}

close_firefoxes

mate-session-save --shutdown-dialog&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Snake in Python 3 + Qt 5</title>
          <pubDate>Fri, 10 Jun 2016 02:09:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/06/10/snake-in-python-3-qt-5/</link>
          <guid>https://artificialworlds.net/blog/2016/06/10/snake-in-python-3-qt-5/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/06/10/snake-in-python-3-qt-5/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;02&#x2F;05&#x2F;snake-in-groovy&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;12&#x2F;snake-in-ruby&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;27&#x2F;snake-in-zx-spectrum-basic&#x2F;&quot;&gt;BASIC&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;03&#x2F;snake-in-dart&#x2F;&quot;&gt;Dart&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, Python3+Qt5&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m writing the game Snake in lots of programming languages, for fun, and to try out new languages.&lt;&#x2F;p&gt;
&lt;p&gt;Python 3 broke compatibility to fix some mistakes - was it worth it? Qt 5 continues to offer more and more features - can it win me over?&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Python 3 + Qt 5: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c08d6160-14d5-4b7a-80be-5e0d9665fd18&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-python3-qt5&#x2F;snake-python3-qt5.html&quot;&gt;Snake in Python 3 + Qt 5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Basic Concepts of Christianity video</title>
          <pubDate>Wed, 18 May 2016 08:35:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/05/18/basic-concepts-of-christianity-video/</link>
          <guid>https://artificialworlds.net/blog/2016/05/18/basic-concepts-of-christianity-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/05/18/basic-concepts-of-christianity-video/">&lt;p&gt;&lt;strong&gt;Update: my ideas have changed quite a bit since I recorded this, especially about the idea that &quot;every crime requires a punishment&quot;. If you&#x27;d like to explore more, try &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;youtu.be&#x2F;2dczm6D1wuY?t=1766&quot;&gt;this sermon by my friend Jörg&lt;&#x2F;a&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;As a Christian myself I thought it might be interesting to try and outline what Christians believe, as I understand it. This is all from a personal perspective, but I hope it&#x27;s helpful:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Basic Concepts of Christianity&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6a005fae-9b9c-4155-8cd7-c89b554c0d99&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;christianity&#x2F;christianity.html#&quot;&gt;Basic Concepts of Christianity&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Elm Basics Video</title>
          <pubDate>Fri, 13 May 2016 18:58:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/05/13/elm-basics-video/</link>
          <guid>https://artificialworlds.net/blog/2016/05/13/elm-basics-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/05/13/elm-basics-video/">&lt;p&gt;Series: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Snake in Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;elm-makes-me-happy-updated-for-elm-0-19-video&#x2F;&quot;&gt;Elm makes me happy&lt;&#x2F;a&gt;, Elm Basics, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;11&#x2F;01&#x2F;writing-a-unit-test-in-elm&#x2F;&quot;&gt;Elm Unit Test&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;10&#x2F;19&#x2F;elm-json-decoder-examples&#x2F;&quot;&gt;Elm JSON&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A lot of the documentation about the new language I am really excited about, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt; focusses on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;guide.elm-lang.org&#x2F;architecture&#x2F;index.html&quot;&gt;Elm Architecture&lt;&#x2F;a&gt; and the ideas of Functional Reactive Programming, and while those are the fundamental reasons I am interested in Elm, I found myself stuck on the syntax quite often.&lt;&#x2F;p&gt;
&lt;p&gt;So, in this video I review almost all the syntax and basic ideas you need to be able to read and write Elm code, treating it as a general programming language.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Elm Basics&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4f3b4fa7-d207-444a-8d7b-6dcaabe69d88&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;strong&gt;Update: forgot to add the slides - here they are:&lt;&#x2F;strong&gt;&lt;br &#x2F;&gt;
Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;elm-basics&#x2F;elm-basics.html&quot;&gt;Elm Basics&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Snake in Elm – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-06-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Elm Basics Video […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>How to analyse a .phd heap dump from an IBM JVM</title>
          <pubDate>Tue, 23 Feb 2016 10:31:57 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/02/23/how-to-analyse-a-phd-heap-dump-from-an-ibm-jvm/</link>
          <guid>https://artificialworlds.net/blog/2016/02/23/how-to-analyse-a-phd-heap-dump-from-an-ibm-jvm/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/02/23/how-to-analyse-a-phd-heap-dump-from-an-ibm-jvm/">&lt;p&gt;If you have been handed a .phd file which is a dump of the heap of an IBM Java virtual machine, you can analyse it using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eclipse.org&#x2F;mat&#x2F;&quot;&gt;Eclipse Memory Analyzer Tool&lt;&#x2F;a&gt; (MAT), but you must install the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.ibm.com&#x2F;developerworks&#x2F;java&#x2F;jdk&#x2F;tools&#x2F;dtfj.html&quot;&gt;IBM Monitoring and Diagnostic Tools&lt;&#x2F;a&gt; first.&lt;&#x2F;p&gt;
&lt;p&gt;Download MAT from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.eclipse.org&#x2F;mat&#x2F;downloads.php&quot;&gt;eclipse.org&#x2F;mat&#x2F;downloads.php&lt;&#x2F;a&gt;. I suggest the Standalone version.&lt;&#x2F;p&gt;
&lt;p&gt;Unzip it and run the MemoryAnalyzer executable inside the zip. Add an argument to control how much memory it gets e.g. to give it 4GB:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;MemoryAnalyzer -vmargs -Xmx4g&lt;&#x2F;pre&gt;
&lt;p&gt;Once it&#x27;s started, go to Help -&amp;gt; Install new software.&lt;&#x2F;p&gt;
&lt;p&gt;Next to &quot;Work with&quot; paste in the URL for the IBM Developer Toolkit update site: http:&#x2F;&#x2F;public.dhe.ibm.com&#x2F;ibmdl&#x2F;export&#x2F;pub&#x2F;software&#x2F;websphere&#x2F;runtimes&#x2F;tools&#x2F;dtfj&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;Click Add...&lt;&#x2F;p&gt;
&lt;p&gt;Type in a name like &quot;IBM Monitoring and Diagnostic Tools&quot; and click OK.&lt;&#x2F;p&gt;
&lt;p&gt;In the list below, an item should appear called IBM Monitoring and Diagnostic Tools. Tick the box next to it, click Next, and follow the wizard to accept the license agreements and install the toolkit.&lt;&#x2F;p&gt;
&lt;p&gt;Restart Eclipse when prompted.&lt;&#x2F;p&gt;
&lt;p&gt;Choose File -&amp;gt; Open Heap Dump and choose your .phd file. It should open in MAT and allow you to figure out who is using all that memory.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stevo
    &lt;span class=&quot;comment_date&quot;&gt;2016-09-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey dude,&lt;&#x2F;p&gt;
&lt;p&gt;Very helpful guide.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Douglas Cardoso
    &lt;span class=&quot;comment_date&quot;&gt;2017-03-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Very nice buddy!&lt;br&#x2F;&gt;
Thank you!&lt;&#x2F;p&gt;
&lt;p&gt;=)&lt;&#x2F;p&gt;
&lt;p&gt;If need any help on HEAPDUMP and JAVACORE for WebSphere products, please contact me!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;dougcardoso21&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;dougcardoso21&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Douglas!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ryan Cox
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for this… IBM product is garbage (no pun intended).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jmjeon
    &lt;span class=&quot;comment_date&quot;&gt;2018-11-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks you !!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Erik Visser
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Poonam Aswani
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;When I tried to update ini file to 4g, it did not open MAT. I needed to reset to what it was that is 1024m and then when I opened this phd file, it gave an error that Error opening heap dump is encountered. Does someone know what to do?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stephen Nagle
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Very helpful , thanks.&lt;&#x2F;p&gt;
&lt;p&gt;Hi Poonam&lt;br&#x2F;&gt;
If you are on windows, type&lt;br&#x2F;&gt;
cmd&lt;br&#x2F;&gt;
in the search , go into command prompt. In the command prompt , change directory (cd) to the directory that MemoryAnalyser.exe is in. Then type&lt;br&#x2F;&gt;
MemoryAnalyzer -vmargs -Xmx4g&lt;br&#x2F;&gt;
and press enter.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ¿Cómo crear un volcado de almacenamiento dinámico compatible con OpenJ9 a través de API? – stack
    &lt;span class=&quot;comment_date&quot;&gt;2022-09-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Para detalles: &lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;02&#x2F;23&#x2F;how-to-analyse-a-phd-heap-dump-from-an-ibm-jvm&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2016&#x2F;02...&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Elm makes me happy (old: Elm 0.17)</title>
          <pubDate>Mon, 08 Feb 2016 14:53:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/02/08/elm-makes-me-happy/</link>
          <guid>https://artificialworlds.net/blog/2016/02/08/elm-makes-me-happy/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/02/08/elm-makes-me-happy/">&lt;p&gt;Updated version: &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;elm-makes-me-happy-updated-for-elm-0-19-video&#x2F;&quot;&gt;Elm makes me happy (updated for Elm 0.19)&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Elm lets you write interactive web sites in a really clean and safe-feeling way, which is a contrast to the feeling I get when using JavaScript and HTML. It&#x27;s functional, but instead of that making everything seem hard, it makes me happy.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Elm makes me happy (old: Elm 0.17)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;370111a0-2c97-4ffa-a22d-18caf06abaae&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;elm-makes-me-happy&#x2F;elm-makes-me-happy.html&quot;&gt;Elm makes me happy&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Snake in Elm – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-05-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Elm makes me happy […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>What&#x27;s new in Java 8</title>
          <pubDate>Tue, 19 Jan 2016 08:40:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/01/19/whats-new-in-java-8/</link>
          <guid>https://artificialworlds.net/blog/2016/01/19/whats-new-in-java-8/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/01/19/whats-new-in-java-8/">&lt;p&gt;Java 8 makes some functional programming ideas available to the rest of us, using Lambda, function references and a streaming style (map, filter, replace etc.):&lt;&#x2F;p&gt;
&lt;iframe title=&quot;What&#x27;s new in Java 8&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c1a13805-b469-4f07-967d-f2178dcb04f8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;whats-new-in-java8&#x2F;whats-new-in-java8.html&quot;&gt;What&#x27;s new in Java 8&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Which Raspberry Pi photo was funniest?</title>
          <pubDate>Mon, 18 Jan 2016 05:03:54 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/01/18/which-raspberry-pi-photo-was-funniest/</link>
          <guid>https://artificialworlds.net/blog/2016/01/18/which-raspberry-pi-photo-was-funniest/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/01/18/which-raspberry-pi-photo-was-funniest/">&lt;p&gt;We had a great day at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;EghamJam&quot;&gt;Egham Raspberry Pi Jam&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; and our &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;16&#x2F;raspberry-pi-funniest-photo-game&#x2F;&quot;&gt;Photo Booth&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;20160117_165257.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;seemed to go down well:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;20160117_145455.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;20160117_153352.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But which photo was funniest? Here are some of the entries (I had to choose kids&#x27; ones without faces to go on here, but there were some other great ones!):&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453019366.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453019915.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453019997.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453020511.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453023139.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But the winner has to be the eyeball wearing a hat!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo1453018765.jpg&quot; alt=&quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thanks everyone, see you next time!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Raspberry Pi Funniest Photo Game</title>
          <pubDate>Sat, 16 Jan 2016 17:03:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/01/16/raspberry-pi-funniest-photo-game/</link>
          <guid>https://artificialworlds.net/blog/2016/01/16/raspberry-pi-funniest-photo-game/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/01/16/raspberry-pi-funniest-photo-game/">&lt;p&gt;For our latest &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;EghamJam&quot;&gt;Egham Raspberry Pi Jam&lt;&#x2F;a&gt;, we worked on a photo booth program, using the Raspberry Pi camera. Here&#x27;s how we did it.&lt;&#x2F;p&gt;
&lt;p&gt;Downloads: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;funniest-photo-game.odp&quot;&gt;funniest-photo-game.odp&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo-booth.zip&quot;&gt;photo-booth.zip&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 1: fixed a bug where it tried to create a directory that already exists&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 2: see the &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;18&#x2F;which-raspberry-pi-photo-was-funniest&#x2F;&quot;&gt;winning photo&lt;&#x2F;a&gt;!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;equipment&quot;&gt;Equipment&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;products&#x2F;raspberry-pi-2-model-b&#x2F;&quot;&gt;Raspberry Pi (1 or 2)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;A USB mouse &amp;amp; keyboard&lt;&#x2F;li&gt;
&lt;li&gt;HDMI screen with HDMI cable&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raspberrypi.org&#x2F;products&#x2F;camera-module&#x2F;&quot;&gt;Raspberry Pi Camera Module&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;what-we-did&quot;&gt;What we did&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Made and decorated a box that held the Pi and camera steady for taking photos:&lt;br &#x2F;&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;box.jpg&quot; alt=&quot;Decorated Box for Raspberry Pi&quot; width=&quot;532&quot; height=&quot;500&quot; class=&quot;alignnone size-full wp-image-1875&quot;&gt;&lt;br &#x2F;&gt;
(Note the holes for the wires!)&lt;&#x2F;li&gt;
&lt;li&gt;Created some costumes (see &quot;Costumes&quot; below)&lt;&#x2F;li&gt;
&lt;li&gt;Wrote a Python program (see &quot;Program&quot; below) to display pictures on top of the camera picture, and take photos&lt;&#x2F;li&gt;
&lt;li&gt;Wrote up the &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;funniest-photo-game.odp&quot;&gt;instructions and competition rules&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;costumes&quot;&gt;Costumes&lt;&#x2F;h3&gt;
&lt;p&gt;We made lots of costumes that show up over the picture. They all needed to be 1280x720 pixels, PNG files that use Indexed Color mode. (In Gimp we clicked &quot;Image&quot;, then &quot;Mode&quot;, then &quot;Indexed Color&quot; before choosing &quot;File&quot; then &quot;Export&quot; or &quot;Overwrite&quot; to save them as .png files.)&lt;&#x2F;p&gt;
&lt;p&gt;Here are some examples:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;1452949023075.png&quot;&gt;&lt;img style=&quot;border: 2px solid black;&quot; width=&quot;533&quot; height=&quot;300&quot; src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;1452949023075-small.png&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;witch-hat.png&quot;&gt;&lt;img style=&quot;border: 2px solid black;&quot; width=&quot;533&quot; height=&quot;300&quot; src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;witch-hat-small.png&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Once we&#x27;d made the costumes we put them in a directory called &quot;costumes&quot; next to the program file, photo-booth.py.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setup&quot;&gt;Setup&lt;&#x2F;h3&gt;
&lt;p&gt;To get the Pi ready to run our program we needed to type these commands:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get update
sudo apt-get install python-picamera
sudo apt-get install python-imaging
&lt;&#x2F;pre&gt;
&lt;p&gt;Once we&#x27;d done this, we created the Python program and directories described in the next section, and then we ran the program with:&lt;&#x2F;p&gt;
&lt;pre&gt;python photo-booth.py
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;program&quot;&gt;Program&lt;&#x2F;h3&gt;
&lt;p&gt;We made a directory to hold our program on the Desktop of our Raspberry Pi, which we called &quot;photo-booth&quot;. Inside that, we made a &quot;costumes&quot; directory containing our costumes, and a &quot;gallery&quot; directory to hold the saved photos. Also inside &quot;photo-booth&quot; we saved this code as &quot;photo-booth.py&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;import io
import picamera
from PIL import Image
import time
import pygame
import subprocess
import os

class PhotoBooth:
    def __init__( self, camera ):
        self.camera = camera
        self.costumes = os.listdir( &#x27;costumes&#x27; )
        self.current_overlay = None
        self.cos_num= 0
        self.current_costume = None

        self.set_up_camera()

        self.change_costume(0)

    def set_up_camera(self):
        self.camera.resolution = ( 1280, 720 )
        self.camera.framerate = 24
        self.camera.start_preview()

    def change_costume(self, change):
        self.cos_num += change
        self.cos_num = self.cos_num%len(self.costumes)
        self.current_costume = Image.open( &#x27;costumes&#x2F;&#x27;+self.costumes[self.cos_num])
        self.overlay(self.current_costume, 128)

    def overlay(self, image, alpha):
        pad = Image.new(&#x27;RGB&#x27;, (
            ((image.size[0] + 31) &#x2F;&#x2F; 32) * 32,
            ((image.size[1] + 15) &#x2F;&#x2F; 16) * 16,
            ))
        pad.paste(image, (0, 0))

        if self.current_overlay is not None:
            self.camera.remove_overlay(self.current_overlay)

        self.current_overlay = camera.add_overlay(pad.tostring(), size=image.size)
        self.current_overlay.alpha = alpha
        self.current_overlay.layer = 3

    def take_photo(self):
        stream = io.BytesIO()
        camera.capture( stream, format=&#x27;jpeg&#x27; )
        stream.seek( 0 )
        captimg = Image.open( stream )
        imgrgba = self.current_costume.convert(&quot;RGBA&quot;)
        captimg.paste( imgrgba, ( 0, -100 ), imgrgba )
        self.overlay(captimg, 255)
        captimg.save( &#x27;gallery&#x2F;photo%d.png&#x27; % time.time().real )
        self.overlay(self.current_costume, 128)

    def run(self):
            while True:
                for event in pygame.event.get():
                    if event.type == pygame.QUIT:
                        return
                    if event.type == pygame.KEYDOWN:
                        if event.key == pygame.K_LEFT:
                            self.change_costume(1)
                        if event.key == pygame.K_RIGHT:
                            self.change_costume(-1)
                        if event.key == pygame.K_SPACE:
                            self.take_photo()
                        if event.key == pygame.K_ESCAPE:
                            return
                        if event.key == pygame.K_g:
                            subprocess.call(&#x27;ls gallery&#x2F;*.png | head -1 | xargs xdg-open&#x27;, shell = True)
                            return
                    if event.type == pygame.MOUSEBUTTONDOWN:
                        self.take_photo()

pygame.init()
pygame.display.set_mode()
if not os.path.exists( &#x27;gallery&#x27; ):
    os.makedirs( &#x27;gallery&#x27; )

with picamera.PiCamera() as camera:
    booth = PhotoBooth( camera )
    booth.run()
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;download-code-and-costumes&quot;&gt;Download code and costumes&lt;&#x2F;h3&gt;
&lt;p&gt;You can download the code and costumes we used here: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;photo-booth.zip&quot;&gt;photo-booth.zip&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you have any questions, feel free to leave a comment below!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Snake in Elm</title>
          <pubDate>Thu, 07 Jan 2016 08:16:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/01/07/snake-in-elm/</link>
          <guid>https://artificialworlds.net/blog/2016/01/07/snake-in-elm/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/01/07/snake-in-elm/">&lt;p&gt;Snake Series: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;02&#x2F;05&#x2F;snake-in-groovy&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;12&#x2F;snake-in-ruby&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;27&#x2F;snake-in-zx-spectrum-basic&#x2F;&quot;&gt;BASIC&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;03&#x2F;snake-in-dart&#x2F;&quot;&gt;Dart&lt;&#x2F;a&gt;, Elm, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;06&#x2F;10&#x2F;snake-in-python-3-qt-5&#x2F;&quot;&gt;Python3+Qt5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Elm Series: Snake in Elm, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;11&#x2F;10&#x2F;elm-makes-me-happy-updated-for-elm-0-19-video&#x2F;&quot;&gt;Elm makes me happy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;05&#x2F;13&#x2F;elm-basics-video&#x2F;&quot;&gt;Elm Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;11&#x2F;01&#x2F;writing-a-unit-test-in-elm&#x2F;&quot;&gt;Elm Unit Test&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2018&#x2F;10&#x2F;19&#x2F;elm-json-decoder-examples&#x2F;&quot;&gt;Elm JSON&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m writing the game Snake in lots of programming languages, for fun, and to try out new languages.&lt;&#x2F;p&gt;
&lt;p&gt;Elm brings the comfortable feeling and scary syntax of a pure functional language to writing dynamic web sites.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Elm: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d35deecd-3e11-4a92-9dda-172f4483de26&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-elm&#x2F;snake-elm.html&quot;&gt;Snake in Elm&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Elm Basics Video – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-05-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Snake in Elm, Elm makes me happy, Elm […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Elm makes me happy – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-05-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Snake in Elm, Elm makes me happy, Elm […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Writing a unit test in Elm – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-11-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Snake in Elm, Elm makes me happy, Elm Basics, Elm Unit […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Out-of-school education settings</title>
          <pubDate>Mon, 04 Jan 2016 20:59:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2016/01/04/out-of-school-education-settings/</link>
          <guid>https://artificialworlds.net/blog/2016/01/04/out-of-school-education-settings/</guid>
          <description xml:base="https://artificialworlds.net/blog/2016/01/04/out-of-school-education-settings/">&lt;p&gt;Here is the comment I sent to the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.gov.uk&#x2F;government&#x2F;consultations&#x2F;out-of-school-education-settings-registration-and-inspection&quot;&gt;Government consultation on out-of-school education settings&lt;&#x2F;a&gt;. Feel free to re-use, modify, distribute as you like.&lt;&#x2F;p&gt;
&lt;p&gt;I am writing to express my opposition to the government&#x27;s plans to regulate the content of out-of-school education.&lt;&#x2F;p&gt;
&lt;p&gt;I volunteer on an annual residential Christian holiday for young people which includes teaching on Christianity and the Bible. I am concerned that the work of this holiday would be regulated under these proposals. However, my concern is much wider than simply this and similar work.&lt;&#x2F;p&gt;
&lt;p&gt;I want to defend people I disagree with, as well as those with whom I agree.&lt;&#x2F;p&gt;
&lt;p&gt;I do not believe it is beneficial to society for governments to control the content of education provided outside of the state education system. In fact, I think it is a dangerous path to take, since while we may trust our current government and inspection regime, we have no reason to suppose it will always be tolerant and respectful of diverse views.&lt;&#x2F;p&gt;
&lt;p&gt;The use of terms such as &quot;British values&quot;, &quot;emotional harm&quot; and &quot;tolerance&quot; in the proposals is concerning since these are open to widely-varying interpretations. In recent history, it could certainly have been considered &quot;un-British&quot; to lobby for the legalisation of homosexuality or the enfranchisement of women, or to support a communist political party.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I interpret &quot;British values&quot; to mean being tolerant of views very different from my own, and keeping state interference in individual decisions to a minimum. In that context, I believe these proposals are in conflict with British values, and my own values.&lt;&#x2F;p&gt;
&lt;p&gt;I believe that the best way to counteract violent religious extremism is to practice the tolerance that we preach: to demonstrate in words and actions that we respect individual freedom of speech and action. Where children are being taught values with which we disagree, we should argue in public against those values, not ban them from being taught.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New open source project on work time - git-history-data</title>
          <pubDate>Mon, 14 Dec 2015 10:00:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/12/14/new-open-source-project-on-work-time-git-history-data/</link>
          <guid>https://artificialworlds.net/blog/2015/12/14/new-open-source-project-on-work-time-git-history-data/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/12/14/new-open-source-project-on-work-time-git-history-data/">&lt;p&gt;Announcing a little open source project that I have built at work and been allowed to publish Freely.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;git-history-data&quot;&gt;git-history-data&lt;&#x2F;a&gt; analyses a Git source code repository and dumps out data in a form that is easy to analyse.&lt;&#x2F;p&gt;
&lt;p&gt;I wrote an article demonstrating how to use it to find out some interesting information about the codebase of Git itself and got it published on IBM DeveloperWorks Open: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20161216005139&#x2F;https:&#x2F;&#x2F;developer.ibm.com&#x2F;open&#x2F;2015&#x2F;12&#x2F;11&#x2F;learning-about-the-git-codebase-using-git-history-data&#x2F;&quot;&gt;Learning about the Git codebase using git-history-data&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Java HTTPS to a server with a self-signed certificate</title>
          <pubDate>Mon, 07 Dec 2015 15:55:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/12/07/java-https-to-a-server-with-a-self-signed-certificate/</link>
          <guid>https://artificialworlds.net/blog/2015/12/07/java-https-to-a-server-with-a-self-signed-certificate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/12/07/java-https-to-a-server-with-a-self-signed-certificate/">&lt;p&gt;Nothing is easy in Java, and nothing is more disproportionately non-easy than downloading something. If you add SSL to the equation, it becomes unfeasible for any human to navigate the twisted passages of the Java API, so here is a tiny fragment of map I have pieced together using the Internets.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Updated to link to my article about &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;05&#x2F;02&#x2F;creating-a-self-signed-certificate-for-apache-and-connecting-to-it-from-java&#x2F;&quot;&gt;how to create a self-signed cert and trust store file&lt;&#x2F;a&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you have a server that you want to download something from, and you need to use SSL (i.e. your URL starts with &quot;https:&#x2F;&#x2F;&quot;), and it has a self-signed certificate, you will need to get hold of a &quot;trust store&quot; file (a .pkcs12 or .jks file) that tells Java it can trust your server. If you need to set this up yourself, try my article &lt;a href=&quot;&#x2F;blog&#x2F;2019&#x2F;05&#x2F;02&#x2F;creating-a-self-signed-certificate-for-apache-and-connecting-to-it-from-java&#x2F;&quot;&gt;how to create a self-signed cert and trust store file&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note that this will only work if the self-signed certificate has the correct Subject Alternative Name (the hostname of the server) embedded in it. The article linked above tells you how to achieve this.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming you have somehow magicked up a trust store file (let&#x27;s call it trust.pkcs12), and you know the password for it (let&#x27;s assume it&#x27;s 000000, as we set up in the linked article), we can continue.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s write a little program Get.java that fetches a URL and tells us whether we got an error with the SSL connection:&lt;&#x2F;p&gt;
&lt;pre&gt;import java.net.URL;
public class Get
{
   public static void main( String[] args ) throws Exception
   {
       try
       {
           new URL( args[0] ).openConnection().getInputStream();
           System.out.println( &quot;Succeeded.&quot; );
       }
       catch( javax.net.ssl.SSLHandshakeException e )
       {
           System.out.println( &quot;SSL exception.&quot; );
       }
   }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Compile this with:&lt;&#x2F;p&gt;
&lt;pre&gt;javac Get.java&lt;&#x2F;pre&gt;
&lt;p&gt;And run it with:&lt;&#x2F;p&gt;
&lt;pre&gt;$ java Get https:&#x2F;&#x2F;google.com
Succeeded.&lt;&#x2F;pre&gt;
&lt;p&gt;This should succeed, because Java knows it can trust the benevolent Google deity, as we all do.&lt;&#x2F;p&gt;
&lt;p&gt;Now try it against your server with a self-signed (or otherwise untrusted) certificate and you should see an error:&lt;&#x2F;p&gt;
&lt;pre&gt;$ java Get https:&#x2F;&#x2F;selfsigned.example.com
SSL exception.&lt;&#x2F;pre&gt;
&lt;p&gt;And now for the answer you were waiting for. You don&#x27;t need to use keytool. Repeat: you don&#x27;t need to use keytool. To run Java telling it to trust your server, just do this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ java \
    -Djavax.net.ssl.trustStore=&#x2F;path&#x2F;to&#x2F;trust.pkcs12 \
    -Djavax.net.ssl.trustStorePassword=000000 \
    Get https:&#x2F;&#x2F;selfsigned.example.com
Succeeded.&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s it.&lt;&#x2F;p&gt;
&lt;p&gt;If it doesn&#x27;t work, try:&lt;&#x2F;p&gt;
&lt;pre&gt;$ java \
    &lt;strong&gt;-Djavax.net.debug=SSL,trustmanager&lt;&#x2F;strong&gt; \
    -Djavax.net.ssl.trustStore=&#x2F;path&#x2F;to&#x2F;trust.pkcs12 \
    -Djavax.net.ssl.trustStorePassword=000000 \
    Get https:&#x2F;&#x2F;selfsigned.example.com
&lt;&#x2F;pre&gt;
&lt;p&gt;and you should see some debugging output that may help.&lt;&#x2F;p&gt;
&lt;p&gt;Another useful debugging command is:&lt;&#x2F;p&gt;
&lt;pre&gt;openssl s_client -showcerts -connect &lt;span style=&quot;color: red;&quot;&gt;selfsigned.example.com&lt;&#x2F;span&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Hope it helps.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    anant
    &lt;span class=&quot;comment_date&quot;&gt;2018-10-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I was trying to import the public cert of selfsigned into existing java cacerts keystore (which is default used by the java), but it didn’t until I did above.  My understanding is all it needs is to trust the cert that I did by importing.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Eddie Kincaid
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for this. It worked a charm! It has proved to be the best little tool for debugging issues, particularly when using self signed certificates, that I have come across.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-08-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Eddie, glad it was helpful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Vim as editor for Claws Mail</title>
          <pubDate>Thu, 03 Dec 2015 09:42:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/12/03/vim-as-editor-for-claws-mail/</link>
          <guid>https://artificialworlds.net/blog/2015/12/03/vim-as-editor-for-claws-mail/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/12/03/vim-as-editor-for-claws-mail/">&lt;p&gt;I got sick of not having &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.vim.org&#x2F;&quot;&gt;Vim&lt;&#x2F;a&gt; as my text editor for emails in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.claws-mail.org&#x2F;&quot;&gt;Claws Mail&lt;&#x2F;a&gt; but GVim makes my eyes bleed so I wanted proper Vim-in-a-terminal as my editor, which was not particularly straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;The proper incantation was to make a script in my PATH called terminal-vim like this:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

mate-terminal --disable-factory -x vim -f &quot;$@&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;(gnome-terminal should work similarly if you&#x27;re in GNOME.)&lt;&#x2F;p&gt;
&lt;p&gt;and to set it as my external editor in Claws Mail by going to Configuration, Preferences, External Programs, and setting Text Editor to terminal-vim &#x27;%s&#x27;.&lt;&#x2F;p&gt;
&lt;p&gt;Then under Writing in Preferences, I ticked the box &quot;Automatically launch the external editor&quot; and all is well.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    David
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello Andy,&lt;&#x2F;p&gt;
&lt;p&gt;I was trying to copy your approach for lxterminal (I am running lubuntu), creating an executable file in my PATH called terminal-vim:&lt;&#x2F;p&gt;
&lt;p&gt;#!&#x2F;bin&#x2F;bash&lt;br&#x2F;&gt;
lxterminal -e vim -f &quot;$@&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I then followed your instructions within the claws-mail preference settings.&lt;&#x2F;p&gt;
&lt;p&gt;Now, when I press the ‘Compose’ button, vim does indeed pop up (in the terminal) and writes to this file:&lt;br&#x2F;&gt;
&quot;~&#x2F;.claws-mail&#x2F;tmp&#x2F;tmpmsg.0x140d930&quot;&lt;&#x2F;p&gt;
&lt;p&gt;However, then I am lost. How do I get my text back into claws-mail?&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for your guidance,&lt;&#x2F;p&gt;
&lt;p&gt;David&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi David, I think if you just :wq the window should close and your text should end up in the Claws-mail window ready to send?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    David
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy, thanks for you reply.&lt;&#x2F;p&gt;
&lt;p&gt;The transfer after :wq does not take place.&lt;&#x2F;p&gt;
&lt;p&gt;In any case, claws-mail expects input in various fields (addressee, title, mail text). How do I provide input to the right place? How does claws-mail know what to do with the text I have written in vim?&lt;&#x2F;p&gt;
&lt;p&gt;I am asking, because I could imagine that I am missing a crucial issue here…&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for your help,&lt;&#x2F;p&gt;
&lt;p&gt;David&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi David, for the To, Subject etc. I’m afraid you just have to type them into Claw-mail.  The message body is what you get to edit in Vim, but if it’s not working I can’t think of why, sorry.  One other possible thought: is lxterminal connection to some existing profile or session?  If so, when you exit, the process would not really end so Claws would not know to continue.  If that’s not it I’m afraid I am out of ideas – sorry.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    simon
    &lt;span class=&quot;comment_date&quot;&gt;2023-04-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello, just posting this for anyone who may find it useful; to use vim with xfce4-terminal, enter the following directly into claws-mail under &quot;set external editor to:&quot;&lt;&#x2F;p&gt;
&lt;p&gt;xfce4-terminal —disable-server -x vim %s&lt;&#x2F;p&gt;
&lt;p&gt;Type your message, then :wq when done.&lt;&#x2F;p&gt;
&lt;p&gt;No script is required for this- thanks for pointing me in the right direction though. Presumably xfce4-terminal may be substituted with the terminal of your choice. Works fine on Claws 4.0.0&lt;&#x2F;p&gt;
&lt;p&gt;This was cobbled together with additional help from the linux mint forum:  &lt;a href=&quot;https:&#x2F;&#x2F;forums.linuxmint.com&#x2F;viewtopic.php?t=235830&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;forums.linuxmint.com&#x2F;viewtopic.php?t=...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thanks to all!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Using GitHub and GitLab</title>
          <pubDate>Wed, 25 Nov 2015 09:55:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/11/25/using-github-and-gitlab/</link>
          <guid>https://artificialworlds.net/blog/2015/11/25/using-github-and-gitlab/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/11/25/using-github-and-gitlab/">&lt;p&gt;Taking all the Git understanding we&#x27;ve developed through the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLgyU3jNA6VjSUZPhZ7WtoBUnxzChBwKuw&quot;&gt;series on Git&lt;&#x2F;a&gt; and applying it to how to work on and contribute to projects on GitHub and similar systems like GitLab:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Using GitHub and GitLab&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fc1176b9-2bda-4b71-af08-b9292564b38d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Avoiding postfix error &quot;status=deferred (unable to look up host&quot;</title>
          <pubDate>Wed, 11 Nov 2015 12:47:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/11/11/avoiding-postfix-error-statusdeferred-unable-to-look-up-host/</link>
          <guid>https://artificialworlds.net/blog/2015/11/11/avoiding-postfix-error-statusdeferred-unable-to-look-up-host/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/11/11/avoiding-postfix-error-statusdeferred-unable-to-look-up-host/">&lt;p&gt;My emails wouldn&#x27;t send when I was working from home, giving me this message in &#x2F;var&#x2F;log&#x2F;mail.log:&lt;&#x2F;p&gt;
&lt;pre&gt;Nov 11 12:38:16 machinename postfix&#x2F;smtp[20672]: CF5D6D41CE2: to=&lt;name@example.com&gt;, relay=none, delay=14416, delays=14416&#x2F;0.01&#x2F;0.07&#x2F;0, dsn=4.3.5, status=deferred (Host or domain name not found. Name service error for name=relay.example.com type=AAAA: Host not found)
&lt;&#x2F;name@example.com&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It turned out I was telling postfix not to use DNS to resolve the domain name of my mail relay server. There were several postfix config options I needed to remove to make this work:&lt;&#x2F;p&gt;
&lt;pre&gt;# Make sure all of these are commented out or removed!
#default_transport = smtp
#relay_transport = smtp
#inet_protocols = ipv4
#smtp_host_lookup = native
&lt;&#x2F;pre&gt;
&lt;p&gt;The default values for these options are fine, but the ones shown above stop it working, so remove them or comment out with a # at the beginning of the line.&lt;&#x2F;p&gt;
&lt;p&gt;More at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.postfix.org&#x2F;postconf.5.html#smtp_host_lookup&quot;&gt;Postfix configuration parameters&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alex M.
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Seems like the issue is that Postfix creates jail before network stack fully initialized, so &#x2F;var&#x2F;spool&#x2F;postfix&#x2F;etc&#x2F;resolv.conf doesn’t have any servers to use and that’s why it fails. I have this issue on my raspberry pi Ubuntu setup and don’t have it on other PC.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Alex, yes – something weird is going on because I still see similar problems sometimes even with this config, but if I specify my relayhost by IP instead of domain name it works fine.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Zhu Ye
    &lt;span class=&quot;comment_date&quot;&gt;2019-02-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;In my case, there is a legacy config from CentOS.&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;disable_dns_lookups = yes
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I use Ubuntu now, I have no idea how it worked in CentOS while it was set like this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Finding the download count of GitHub releases</title>
          <pubDate>Fri, 16 Oct 2015 03:32:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/10/16/finding-the-download-count-of-github-releases/</link>
          <guid>https://artificialworlds.net/blog/2015/10/16/finding-the-download-count-of-github-releases/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/10/16/finding-the-download-count-of-github-releases/">&lt;p&gt;You can use the GitHub API to find out how many times the files in your releases have been downloaded.&lt;&#x2F;p&gt;
&lt;p&gt;For example, to find out how many downloads there have been of my Rabbit Escape project you can do:&lt;&#x2F;p&gt;
&lt;pre&gt;curl -s https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;releases | egrep &#x27;&quot;name&quot;|&quot;download_count&quot;&#x27;&lt;&#x2F;pre&gt;
&lt;p&gt;Or you can look through the information manually by visiting a URL like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;releases&quot;&gt;https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;releases&lt;&#x2F;a&gt; in your browser.&lt;&#x2F;p&gt;
&lt;p&gt;To get the total I came up with this beautiful incantation:&lt;&#x2F;p&gt;
&lt;pre&gt;curl -s https:&#x2F;&#x2F;api.github.com&#x2F;repos&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;releases | egrep &#x27;download_count&#x27;  | cut &#x27;-d:&#x27; -f 2 | sed &#x27;s&#x2F;,&#x2F;+&#x2F;&#x27; | xargs echo | xargs -I N echo N 0  | bc&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Zmn
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Can I suggest this app to list all stats about your repository? &lt;a href=&quot;http:&#x2F;&#x2F;github-analytics.apphb.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;github-analytics.apphb.com&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Disclaimer: I’m the owner of the application. Please share your opinion about it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Zmn, thanks – it looks useful, but I would definitely like the projects to be sorted in descending order of downloads – I have a &lt;em&gt;lot&lt;&#x2F;em&gt; of zeroes…. :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Zmn
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great, It’s nice you find it useful. I’m planning to improve usability and add some features… I’m working on it, stay tuned !&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sarita
    &lt;span class=&quot;comment_date&quot;&gt;2021-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi. I’m trying to use your total command on osx but I get an error:&lt;&#x2F;p&gt;
&lt;p&gt;(standard_in) 1: illegal character: N.&lt;&#x2F;p&gt;
&lt;p&gt;Any suggestion? thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2021-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Sarita, it looks like xargs works differently on your platform.  I’m afraid I don’t know what to do to fix it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Daniel
    &lt;span class=&quot;comment_date&quot;&gt;2025-08-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Have a look at the following website, it shows detailed Github release stats: &lt;a href=&quot;https:&#x2F;&#x2F;github-release-stats.ghostbyte.dev&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github-release-stats.ghostbyte.dev&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Android: using a TextView to show rich text in an AlertDialog</title>
          <pubDate>Sat, 10 Oct 2015 03:42:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/10/10/android-using-a-textview-to-show-rich-text-in-an-alertdialog/</link>
          <guid>https://artificialworlds.net/blog/2015/10/10/android-using-a-textview-to-show-rich-text-in-an-alertdialog/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/10/10/android-using-a-textview-to-show-rich-text-in-an-alertdialog/">&lt;p&gt;If you want to display a link or basic formatting in an AlertDialog on Android, you can do it by providing HTML.&lt;&#x2F;p&gt;
&lt;p&gt;The key parts you need are &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;reference&#x2F;android&#x2F;text&#x2F;Html.html#fromHtml%28java.lang.String%29&quot;&gt;Html.fromHtml&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;reference&#x2F;android&#x2F;widget&#x2F;TextView.html#setMovementMethod%28android.text.method.MovementMethod%29&quot;&gt;TextView.setMovementMethod&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Make sure you pass the dialog&#x27;s context in to the constructor of the TextView, not the context of the current activity. Otherwise the colours in your TextView will be wrong and you may well end up with black text on a dark grey background.&lt;&#x2F;p&gt;
&lt;pre&gt;AlertDialog dialog = new AlertDialog.Builder( activity )
    .setTitle( t( world.name ) )
    .setPositiveButton( &quot;Yes!&quot; )
    .setNeutralButton( &quot;Maybe?&quot; )
    .create();

TextView view = new TextView( dialog.getContext() );
view.setText( Html.fromHtml( &quot;&amp;lt;b&amp;gt;foo&amp;lt;&#x2F;b&amp;gt; &amp;lt;a href=&#x27;#&#x27;&amp;gt;bar&amp;lt;&#x2F;a&amp;gt;&quot; ) );
view.setMovementMethod( LinkMovementMethod.getInstance() );
view.setPadding( 10, 10, 10, 10 );

dialog.setView( view );
dialog.show();
&lt;&#x2F;pre&gt;
&lt;p&gt;If you are on API level 11+, you can use AlertDialog.Builder&#x27;s getContext() method, so you don&#x27;t have to create the dialog until the end.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Assassination of individuals by the state</title>
          <pubDate>Wed, 30 Sep 2015 15:45:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/09/30/assassination-of-individuals-by-the-state/</link>
          <guid>https://artificialworlds.net/blog/2015/09/30/assassination-of-individuals-by-the-state/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/09/30/assassination-of-individuals-by-the-state/">&lt;p&gt;I &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;09&#x2F;08&#x2F;letter-to-my-mp-on-uk-government-assassination-of-british-citizens-in-syria&#x2F;&quot;&gt;wrote to my MP&lt;&#x2F;a&gt; (via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&quot;&gt;writetothem.com&lt;&#x2F;a&gt;) about the British government assassinating people they suspected of planning terrorist attacks. He replied saying that the government believes the action was legal, proportional and necessary. Part of this justification was based on the fact that circumstances in Syria make it difficult to disrupt attacks in other ways. He stated the people killed were recruiting ISIL members and planning specific attacks in Britain. He also stated that this action was unconnected to general military action against ISIL in Syria.&lt;&#x2F;p&gt;
&lt;p&gt;My reply is below. Feel free to re-use and adapt it if you want to send a similar letter.&lt;&#x2F;p&gt;
&lt;p&gt;Dear Philip Hammond,&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for your reply to my letter about the assassination of Junaid Hussain and Reyaad Khan.&lt;&#x2F;p&gt;
&lt;p&gt;I have 4 questions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;When did the British government start assassinating individuals suspected of planning terrorist attacks?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;What standard of evidence is required to assassinate someone?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;How imminent must a suspected attack be to warrant assassination?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;What oversight is there of each decision to perform an assassination?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I continue to believe this practice is in conflict with British values and international law. You mention the UN Charter: I assume you are referring to Article 51 of the UN Charter, which refers to &quot;armed attack&quot; against a country. I do not accept that an individual suspected of planning a terrorist attack falls under the definition of an &quot;armed attack&quot; which was surely a reference to warfare by state-like entities, rather than plans being made by individuals to commit acts well-covered by UK criminal law.&lt;&#x2F;p&gt;
&lt;p&gt;We celebrated the 800th anniversary of Magna Carta in your constituency this year. Our reasons for celebration included the limitation of the state&#x27;s power over the individual. When the state executes individuals without trial or even publication of evidence, is there any practical limit to its power?&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Difficult merges in Git - don&#x27;t panic!</title>
          <pubDate>Thu, 10 Sep 2015 10:38:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/09/10/difficult-merges-in-git-dont-panic/</link>
          <guid>https://artificialworlds.net/blog/2015/09/10/difficult-merges-in-git-dont-panic/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/09/10/difficult-merges-in-git-dont-panic/">&lt;p&gt;A video in which I try to explain what merging and rebasing really are, to help you understand what is going on when Git presents you with scary-looking conflict messages. I also explain why you shouldn&#x27;t panic because it&#x27;s hard to lose your work, and how to get you work back if you really mess up:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Difficult merges in git - don&#x27;t panic!&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;bc5ce190-cc6f-44d2-8dbd-56bd2570500f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;difficult-merges-in-git&#x2F;difficult-merges-in-git.html&quot;&gt;Difficult Merges in Git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;A commit represents the state of the world (and the history leading up to that state). A commit is not a diff.&lt;&#x2F;p&gt;
&lt;p&gt;Merging means making a new commit with two (or more) &quot;parents&quot; (previous commits) that represents the result of merging the changes from two different threads of development that happened separately. None of the already-committed commits are modified - you just get a new commit on top. History is more complicated, but true.&lt;&#x2F;p&gt;
&lt;p&gt;Rebasing means modifying the history of one thread of development so it looks like it happened after the other one. This involves modifying all the commits in that thread. There is no extra merge commit, so you lose the history of the merge that happened. History is simple, but it&#x27;s a lie, and if you messed up the rebasing process, you can&#x27;t get back to where you were (once your old commits have been garbage-collected).&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ZX Spectrum BASIC Web Server</title>
          <pubDate>Wed, 09 Sep 2015 10:22:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/09/09/zx-spectrum-basic-web-server/</link>
          <guid>https://artificialworlds.net/blog/2015/09/09/zx-spectrum-basic-web-server/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/09/09/zx-spectrum-basic-web-server/">&lt;p&gt;Finally, you can write your web sites in ZX Spectrum BASIC, using my new project, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;spectrum-basic-httpd&quot;&gt;ZX Spectrum BASIC CGI server &lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-it-works&quot;&gt;How it works&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s what happens when a request comes in:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Apache HTTPD receives the request, and runs the CGI script that does the magic.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;spectrum-basic-httpd&#x2F;blob&#x2F;master&#x2F;spectrum-basic.cgi&quot;&gt;CGI script&lt;&#x2F;a&gt; (written in Bash) generates some BASIC code that provides the HTTP meta-vars (e.g. PATH_INFO for the path of the request) as DATA statements and wraps some other boilerplate around the code of the .basic file asked for in the request, and writes out another .basic file which is ready to be executed.&lt;&#x2F;li&gt;
&lt;li&gt;Then it uses &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.worldofspectrum.org&#x2F;utilities.html&quot;&gt;BAS2TAP&lt;&#x2F;a&gt; (my mirror &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;bas2tap&quot;&gt;here&lt;&#x2F;a&gt;) to create an audio tape file representing the program.&lt;&#x2F;li&gt;
&lt;li&gt;Next it launches the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;fuse-emulator.sourceforge.net&#x2F;&quot;&gt;Fuse&lt;&#x2F;a&gt; spectrum emulator (inside an Xvfb wrapper to make it think it has a display), telling it to load from the audio tape and run the program.&lt;&#x2F;li&gt;
&lt;li&gt;The program runs, and writes its HTTP responses to the Spectrum printer using LPRINT statements.&lt;&#x2F;li&gt;
&lt;li&gt;Fuse uses very basic OCR to understand what characters are printed, and writes them into a text file.&lt;&#x2F;li&gt;
&lt;li&gt;The CGI script monitors the output file, waiting for a line containing an end marker (&quot;ENDSPECTRUMBASIC&quot;). When it finds it, it kills the emulator (since it can&#x27;t made to auto-exit, I think).&lt;&#x2F;li&gt;
&lt;li&gt;The CGI script sends the contents of the output file (minus the end marker) back to Apache, which returns it as a response.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Simple.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why&quot;&gt;Why?&lt;&#x2F;h3&gt;
&lt;p&gt;Originally designed to demonstrate how &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;&quot;&gt;Docker&lt;&#x2F;a&gt; can help isolate other services from &quot;interesting&quot; dependencies, this became a bit of a labour of love.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll never stop being grateful to the designers of the ZX Spectrum, or the authors of &quot;Further Programming for the ZX Spectrum&quot;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ZX Spectrum BASIC CGI server – Retro Hardware
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Documentation:ZX Spectrum BASIC Web Server […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP on UK government assassination of British Citizens in Syria</title>
          <pubDate>Tue, 08 Sep 2015 07:48:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/09/08/letter-to-my-mp-on-uk-government-assassination-of-british-citizens-in-syria/</link>
          <guid>https://artificialworlds.net/blog/2015/09/08/letter-to-my-mp-on-uk-government-assassination-of-british-citizens-in-syria/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/09/08/letter-to-my-mp-on-uk-government-assassination-of-british-citizens-in-syria/">&lt;p&gt;Below is a copy of the letter I sent to my MP this morning (via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&quot;&gt;writetothem.com&lt;&#x2F;a&gt;). Feel free to re-use and adapt it if you want to send a similar letter.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;09&#x2F;30&#x2F;assassination-of-individuals-by-the-state&#x2F;&quot;&gt;follow-up letter&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Dear Philip Hammond,&lt;&#x2F;p&gt;
&lt;p&gt;I was extremely concerned to hear of the assassination of two British citizens by the UK armed forces [1].&lt;&#x2F;p&gt;
&lt;p&gt;The Prime Minister and Defence Secretary justified the attack on the basis of self-defence, which I believe is a reference to Article 51 of the UN Charter [2]. I consider the use of this article to cover action against individuals who are suspected of planning domestic terrorism to be wholly inappropriate. I support those who are calling for a legal challenge against this action.&lt;&#x2F;p&gt;
&lt;p&gt;Please pass on my concerns to the Prime Minister and Defence Secretary, and use your considerable influence in this area to make government policy focus on the use of criminal law to prevent domestic terrorism.&lt;&#x2F;p&gt;
&lt;p&gt;Please work to ensure the government abides by the decision of the UK parliament not to participate in military action in Syria, and thus not to treat this situation as a war in which the UK is participating. As you said on 2nd September 2013, &quot;The House of Commons has ruled out military participation in any such response&quot; (referring to our response to the use of chemical weapons).&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
&lt;p&gt;[1] &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;uk-34181475&quot;&gt;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;news&#x2F;uk-34181475&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
[2] &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikisource.org&#x2F;wiki&#x2F;Charter_of_the_United_Nations#Article_51&quot;&gt;https:&#x2F;&#x2F;en.wikisource.org&#x2F;wiki&#x2F;Charter_of_the_United_Nations#Article_51&lt;&#x2F;a&gt;&lt;br &#x2F;&gt;
[3] &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.publications.parliament.uk&#x2F;pa&#x2F;cm201314&#x2F;cmhansrd&#x2F;cm130902&#x2F;debtext&#x2F;130902-0001.htm#1309025000394&quot;&gt;http:&#x2F;&#x2F;www.publications.parliament.uk&#x2F;pa&#x2F;cm201314&#x2F;cmhansrd&#x2F;cm130902&#x2F;debtext&#x2F;130902-0001.htm#1309025000394&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape out now on Android!</title>
          <pubDate>Sun, 30 Aug 2015 11:11:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/08/30/rabbit-escape-out-now-on-android/</link>
          <guid>https://artificialworlds.net/blog/2015/08/30/rabbit-escape-out-now-on-android/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/08/30/rabbit-escape-out-now-on-android/">&lt;p&gt;Watch the video:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rabbit Escape out now on Android!&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;472235a1-337c-452c-834c-87b89dbf549f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Buy the game on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;play.google.com&#x2F;store&#x2F;apps&#x2F;details?id=net.artificialworlds.rabbitescape&quot;&gt;Play Store&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Changing the Docker daemon options in systemd on Ubuntu 15.04</title>
          <pubDate>Thu, 27 Aug 2015 11:48:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/08/27/changing-the-docker-daemon-options-in-systemd-on-ubuntu-15-04/</link>
          <guid>https://artificialworlds.net/blog/2015/08/27/changing-the-docker-daemon-options-in-systemd-on-ubuntu-15-04/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/08/27/changing-the-docker-daemon-options-in-systemd-on-ubuntu-15-04/">&lt;p&gt;&lt;strong&gt;Update: now documenting the better way, as described in issue &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;docker&#x2F;pull&#x2F;14513&quot;&gt;14513&lt;&#x2F;a&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 2: I think this way is even better (works in Ubuntu 16.04, Docker 1.12.2, Reference: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;engine&#x2F;reference&#x2F;commandline&#x2F;dockerd&#x2F;&quot;&gt;dockerd command line&lt;&#x2F;a&gt;):&lt;&#x2F;strong&gt;&lt;pre&gt;sudo -s -H
echo &#x27;{&quot;insecure-registries&quot;:[&quot;myreg.example.com:5000&quot;]}&#x27; &amp;gt; &#x2F;etc&#x2F;docker&#x2F;daemon.json
exit
sudo service docker restart&lt;&#x2F;pre&gt;&lt;strong&gt;End of update 2&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On earlier versions of Ubuntu (14.04 and before), changing the command line options to the Docker daemon (e.g. to allow using an insecure private registry) was just a matter of editing &#x2F;etc&#x2F;default&#x2F;docker and uncommenting the line starting with #DOCKER_OPTS=.&lt;&#x2F;p&gt;
&lt;p&gt;On Ubuntu 15.04, even though that file still exists, editing it does not have any effect, and I found it quite tricky to work out how to change Docker&#x27;s command line, so I wrote it up here.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to use an insecure private docker registry for Docker on Ubuntu 15.04, which uses systemd.&lt;&#x2F;p&gt;
&lt;p&gt;Under systemd, we must create a config file that overrides the default Docker command line by typing sudo systemctl edit docker. In the editor which pops up, type:&lt;pre&gt;[Service]
ExecStart=
ExecStart=&#x2F;usr&#x2F;bin&#x2F;docker daemon -H fd:&#x2F;&#x2F; --insecure-registry=myreg.example.com:5000
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note: the first &quot;ExecStart=&quot; line is necessary.&lt;&#x2F;p&gt;
&lt;p&gt;The second &quot;ExecStart=&quot; line should contain whatever command line options you want to include.&lt;&#x2F;p&gt;
&lt;p&gt;Note: for docker versions before 1.10, replace &quot;daemon&quot; with &quot;-d&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;With this config in place, restart the Docker service:&lt;&#x2F;p&gt;
&lt;pre&gt;$ sudo systemctl restart docker
&lt;&#x2F;pre&gt;
&lt;p&gt;Check everything looks right for the Docker service:&lt;&#x2F;p&gt;
&lt;pre&gt;$ systemctl status docker
&lt;&#x2F;pre&gt;
&lt;p&gt;And confirm the command line arguments have been applied with:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ps axwww | grep &#x2F;usr&#x2F;bin&#x2F;docker
&lt;&#x2F;pre&gt;
&lt;p&gt;The instructions here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;articles&#x2F;systemd&#x2F;&quot;&gt;Control and configure Docker with systemd&lt;&#x2F;a&gt; and issues: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;docker&#x2F;pull&#x2F;14513&quot;&gt;14513&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;docker&#x2F;docker&#x2F;issues&#x2F;15859&quot;&gt;15859&lt;&#x2F;a&gt; suggest that the Docker team are not planning to make this any easier in the short term.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Docker fails to start on Ubuntu 15.04</title>
          <pubDate>Fri, 17 Jul 2015 09:32:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/07/17/docker-fails-to-start-on-ubuntu-15-04/</link>
          <guid>https://artificialworlds.net/blog/2015/07/17/docker-fails-to-start-on-ubuntu-15-04/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/07/17/docker-fails-to-start-on-ubuntu-15-04/">&lt;p&gt;I installed Docker on Ubuntu 15.04 using:&lt;&#x2F;p&gt;
&lt;pre&gt;wget -qO- https:&#x2F;&#x2F;get.docker.com&#x2F; | sh&lt;&#x2F;pre&gt;
&lt;p&gt;as described at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;installation&#x2F;ubuntulinux&#x2F;#installation&quot;&gt;Install Docker on Ubuntu&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I added myself to the docker group:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo usermod -aG docker balaaman&lt;&#x2F;pre&gt;
&lt;p&gt;Then I logged out and logged in again, and ran:&lt;&#x2F;p&gt;
&lt;pre&gt;docker run hello-world&lt;&#x2F;pre&gt;
&lt;p&gt;and saw this:&lt;&#x2F;p&gt;
&lt;pre&gt;Cannot connect to the Docker daemon. Is &#x27;docker -d&#x27; running on this host?&lt;&#x2F;pre&gt;
&lt;p&gt;When I tried to start the Docker daemon like this:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo service docker start&lt;&#x2F;pre&gt;
&lt;p&gt;I got this error:&lt;&#x2F;p&gt;
&lt;pre&gt;Failed to start docker.service: Unit docker.service is masked.&lt;&#x2F;pre&gt;
&lt;p&gt;Thanks to Yannick Lizzi on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;groups.google.com&#x2F;forum&#x2F;#!topic&#x2F;docker-user&#x2F;3Sou7Umo0SA&quot;&gt;this thread&lt;&#x2F;a&gt; I found this fix:&lt;&#x2F;p&gt;
&lt;pre&gt;systemctl unmask docker.service
systemctl unmask docker.socket
systemctl start docker.service
&lt;&#x2F;pre&gt;
&lt;p&gt;After this, I logged out and logged in again (again) and &quot;docker run hello-world&quot; worked fine, and all was well.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nicholas Maietta
    &lt;span class=&quot;comment_date&quot;&gt;2016-06-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You may want to consider updating this blog post to mention that the user will need to re-authenticate their session to pick up the changes if they cannot simply use the docker command without sudo, after making the aforementioned changes.&lt;&#x2F;p&gt;
&lt;p&gt;That is all, thank you.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Nicholas – I mentioned logging out and logging in again earlier (after the usermod command) – do you need to do it again after the systemctl commands?  I don’t think I needed to…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    amin
    &lt;span class=&quot;comment_date&quot;&gt;2016-11-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    fay
    &lt;span class=&quot;comment_date&quot;&gt;2016-12-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;it worked!!&lt;br&#x2F;&gt;
and very clearly described, thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Luciano
    &lt;span class=&quot;comment_date&quot;&gt;2018-03-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you very much, help appreciated :D&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-03-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Glad it helped :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    JesÃºs Virseda Jerez
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;do you need to do it again after the systemctl commands?&quot;&lt;br&#x2F;&gt;
Yes, you need  ;)&lt;br&#x2F;&gt;
And teras and teras of thanks for your post Andy Balaam.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, updated!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Renata Campos
    &lt;span class=&quot;comment_date&quot;&gt;2018-09-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Perfeito!!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    brawaga
    &lt;span class=&quot;comment_date&quot;&gt;2019-06-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Did not help. Still masked.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Preventing Audacity from crashing when using PulseAudio</title>
          <pubDate>Fri, 03 Jul 2015 14:44:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/07/03/preventing-audacity-from-crashing-when-using-pulseaudio/</link>
          <guid>https://artificialworlds.net/blog/2015/07/03/preventing-audacity-from-crashing-when-using-pulseaudio/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/07/03/preventing-audacity-from-crashing-when-using-pulseaudio/">&lt;p&gt;I found that Audacity would crash whenever the Playback Device in the Devices section of Preferences was set to &quot;pulse&quot; or &quot;default&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;This can be fixed by launching Audacity like this:&lt;&#x2F;p&gt;
&lt;pre&gt;Exec=env PULSE_LATENCY_MSEC=100 audacity&lt;&#x2F;pre&gt;
&lt;p&gt;I fixed it &quot;permanently&quot; by modifying my Audacity launcher file, which is in &#x2F;usr&#x2F;share&#x2F;applications&#x2F;audacity.desktop.&lt;&#x2F;p&gt;
&lt;p&gt;I changed the line starting &quot;Exec=&quot; to:&lt;&#x2F;p&gt;
&lt;pre&gt;Exec=env PULSE_LATENCY_MSEC=100 audacity %F&lt;&#x2F;pre&gt;
&lt;p&gt;More info is on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;forum.audacityteam.org&#x2F;viewtopic.php?f=48&amp;amp;t=81315&quot;&gt;Audacity forum&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Movie podcast &quot;The Good Robot Andys&quot;</title>
          <pubDate>Fri, 26 Jun 2015 15:04:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/26/movie-podcast-the-good-robot-andys/</link>
          <guid>https://artificialworlds.net/blog/2015/06/26/movie-podcast-the-good-robot-andys/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/26/movie-podcast-the-good-robot-andys/">&lt;p&gt;My friend and I have launched a new podcast in which we discuss movies: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;goodrobotandys&#x2F;&quot;&gt;The Good Robot Andys&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;goodrobotandys&#x2F;&quot;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;goodrobotandys.png&quot; alt=&quot;The Good Robot Andys&quot; width=&quot;385&quot; height=&quot;288&quot; class=&quot;alignnone size-full wp-image-1783&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Code for detecting when you leave an Android app</title>
          <pubDate>Thu, 18 Jun 2015 08:22:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/18/code-for-detecting-when-you-leave-an-android-app/</link>
          <guid>https://artificialworlds.net/blog/2015/06/18/code-for-detecting-when-you-leave-an-android-app/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/18/code-for-detecting-when-you-leave-an-android-app/">&lt;p&gt;Further to &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;05&#x2F;detecting-whether-an-android-app-is-stopping-or-starting&#x2F;&quot;&gt;Detecting whether an Android app is stopping (or starting)&lt;&#x2F;a&gt;, I implemented code to decide when you are leaving or entering my game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The relevant class is called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;blob&#x2F;d30a7fbdcb286fe4351780b1d14287f008de1ecb&#x2F;rabbit-escape-render&#x2F;src&#x2F;rabbitescape&#x2F;render&#x2F;androidutil&#x2F;Lifecycle2SoundEvents.java&quot;&gt;Lifecycle2SoundEvents&lt;&#x2F;a&gt;. (Yes, it&#x27;s a terrible name. Yes, I spent a long time trying to name it, and this is the best I came up with.)&lt;&#x2F;p&gt;
&lt;p&gt;And the tests, which are in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&#x2F;blob&#x2F;d30a7fbdcb286fe4351780b1d14287f008de1ecb&#x2F;rabbit-escape-render&#x2F;test&#x2F;rabbitescape&#x2F;render&#x2F;androidutil&#x2F;TestLifecycle2SoundEvents.java&quot;&gt;TestLifecycle2SoundEvents&lt;&#x2F;a&gt;, look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;@Test
public void Press_the_home_button_api10_causes_pause()
{
    Tester t = new Tester( activity1 );

    t.in.onSaveInstanceState( activity1 );
    t.in.onPause( activity1 );
    t.in.onStop( activity1 );

    &#x2F;&#x2F; When we press home, we must at least pause (really we stop)
    t.assertPaused();
}
&lt;&#x2F;pre&gt;
&lt;p&gt;which I was reasonably pleased with, because they match my original blog post &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2015&#x2F;06&#x2F;05&#x2F;order-of-android-activity-lifecycle-events&#x2F;&quot;&gt;Order of Android Activity lifecycle events&lt;&#x2F;a&gt; fairly well, without too much noise.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mocks are Bad, Layers are Bad</title>
          <pubDate>Wed, 10 Jun 2015 08:33:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/10/mocks-are-bad-layers-are-bad/</link>
          <guid>https://artificialworlds.net/blog/2015/06/10/mocks-are-bad-layers-are-bad/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/10/mocks-are-bad-layers-are-bad/">&lt;p&gt;In which I argue that mocks are a code smell, and layers lead to increased coupling:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;2108&quot;&gt;Mocks are Bad, Layers are Bad&lt;&#x2F;a&gt; (in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;&quot;&gt;ACCU&lt;&#x2F;a&gt;&#x27;s Overload Journal issue 127)&lt;&#x2F;p&gt;
&lt;p&gt;I also suggest some ways to avoid both mocks and layers, including Classical TDD, Selfish Object, Refactor to Functional and, of course, the Unix Philosophy. I work through a code example to demonstrate some of these things.&lt;&#x2F;p&gt;
&lt;p&gt;I also suggest that frameworks and inheritance hierarchies are bad, but the title was getting too long already.&lt;&#x2F;p&gt;
&lt;p&gt;You can also get the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;var&#x2F;uploads&#x2F;journals&#x2F;Overload127.pdf&quot;&gt;PDF of Overload 127&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Prediction result: corporate Linux</title>
          <pubDate>Mon, 08 Jun 2015 19:09:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/08/prediction-result-corporate-linux/</link>
          <guid>https://artificialworlds.net/blog/2015/06/08/prediction-result-corporate-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/08/prediction-result-corporate-linux/">&lt;p&gt;Ten years ago I predicted that &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2005&#x2F;06&#x2F;09&#x2F;prediction-corporate-linux&#x2F;&quot;&gt;30% of corporate desktops would be Linux&lt;&#x2F;a&gt; or similar open source desktops.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s very hard to find any accurate (or even confident) numbers, but it seems clear I was wrong.&lt;&#x2F;p&gt;
&lt;p&gt;This month, NetMarketShare puts &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.netmarketshare.com&#x2F;operating-system-market-share.aspx?qprid=9&amp;amp;qpcustomb=0&amp;amp;qpsp=173&amp;amp;qpnp=24&amp;amp;qptimeframe=M&quot;&gt;Linux usage at 1.28%&lt;&#x2F;a&gt;, and w3schools puts &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.w3schools.com&#x2F;browsers&#x2F;browsers_os.asp&quot;&gt;Linux usage at 5.2%&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I think companies almost certainly use Linux more than home consumers. Certainly, my own company has a very large section of its population using Linux - all users who have access to sensitive information are required to use it to increase security.&lt;&#x2F;p&gt;
&lt;p&gt;But, I would be very surprised if levels were at 30%.&lt;&#x2F;p&gt;
&lt;p&gt;Linux desktop usage does continue to creep upwards though, while everyone is distracted looking at phones and tablets. At some time there may be a tipping point.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Detecting whether an Android app is stopping (or starting)</title>
          <pubDate>Fri, 05 Jun 2015 01:14:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/05/detecting-whether-an-android-app-is-stopping-or-starting/</link>
          <guid>https://artificialworlds.net/blog/2015/06/05/detecting-whether-an-android-app-is-stopping-or-starting/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/05/detecting-whether-an-android-app-is-stopping-or-starting/">&lt;p&gt;I am writing an Android app (called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;), and I want it to start playing music when the user enters the app, and stop when the user leaves.&lt;&#x2F;p&gt;
&lt;p&gt;Not as easy as it sounds because Android largely doesn&#x27;t think in apps, but &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;guide&#x2F;components&#x2F;activities.html&quot;&gt;Activities&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; real-life code for this is here: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;18&#x2F;code-for-detecting-when-you-leave-an-android-app&#x2F;&quot;&gt;Code for detecting when you leave an Android app&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In my previous post &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;05&#x2F;order-of-android-activity-lifecycle-events&#x2F;&quot;&gt;Order of Android Activity lifecycle events&lt;&#x2F;a&gt; I tracked the methods that get called on activities on my two test devices when the user does various actions. There was some variation between devices, but we can determine some useful rules.&lt;&#x2F;p&gt;
&lt;p&gt;First, let&#x27;s enumerate what we need to detect.&lt;&#x2F;p&gt;
&lt;p&gt;The user leaves by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;pressing &quot;home&quot; at any time&lt;&#x2F;li&gt;
&lt;li&gt;pressing the power button when the app is running&lt;&#x2F;li&gt;
&lt;li&gt;pressing &quot;back&quot; when in the first activity&lt;&#x2F;li&gt;
&lt;li&gt;launching an external activity (e.g. web browser)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The user enters by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;starting the app (e.g. from the home page, or the &quot;running apps&quot; panel)&lt;&#x2F;li&gt;
&lt;li&gt;pressing the power button when the app has been suspended&lt;&#x2F;li&gt;
&lt;li&gt;pressing &quot;back&quot; when in an external activity launched by us&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From the previous post we can work out these rules:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When the user is entering, onResume is always called (on the activity that will be current)&lt;&#x2F;li&gt;
&lt;li&gt;When the user is leaving, onStop is always called (on the current activity), except when pressing the power button (to turn off) where you may only see an onPause call.&lt;&#x2F;li&gt;
&lt;li&gt;When the user is moving between activities, onResume is always called on the new activity, and onStop is always called on the old one. The onResume call is always before onStop&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Thus, if we are OK with leaving ourselves running when the power button is pressed, we can detect leaving and entering the app using the code below.&lt;&#x2F;p&gt;
&lt;p&gt;If we need to stop something when the power button is pressed too, it seems we must be prepared to stop it when we receive onPause, and immediately restart it if we receive onResume, hoping that doesn&#x27;t cause a problem (e.g. a break in the music).&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; All activities have these two methods:
@Override
public void onResume()
{
    super.onResume();
    LeavingOrEntering.activityResumed( this );
}

@Override
public void onStop()
{
    super.onStop();
    LeavingOrEntering.activityStopped( this );
}

&#x2F;&#x2F; And we detect leaving and entering like this:
class LeavingOrEntering
{
    private static Activity currentActivity = null;

    public static void activityResumed( Activity activity )
    {
        if ( currentActivity == null )
        {
            &#x2F;&#x2F; We were resumed and no-one else was running.
            notifyEntering() &#x2F;&#x2F; Start the music!
        }
        currentActivity = activity;
    }

    public static void activityStopped( Activity activity )
    {
        assert currentActivity != null;

        if ( currentActivity == activity )  
        {
            &#x2F;&#x2F; We were stopped and no-one else has been started.
            notifyLeaving(); &#x2F;&#x2F; Stop the music!
        }
        currentActivity == null;
    }
}
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Order of Android Activity lifecycle events</title>
          <pubDate>Fri, 05 Jun 2015 00:31:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/05/order-of-android-activity-lifecycle-events/</link>
          <guid>https://artificialworlds.net/blog/2015/06/05/order-of-android-activity-lifecycle-events/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/05/order-of-android-activity-lifecycle-events/">&lt;p&gt;I noticed some variation between devices so I tested various user actions on various devices and recorded the Android Activity lifecycle methods (e.g. onResume, onPause, onCreate, onStop) that got called. My results are below.&lt;&#x2F;p&gt;
&lt;p&gt;[Why? I want to detect whether the user is leaving the app or just transitioning from one activity to another. See my next blog posts &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;05&#x2F;detecting-whether-an-android-app-is-stopping-or-starting&#x2F;&quot;&gt;Detecting whether an Android app is stopping (or starting)&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;18&#x2F;code-for-detecting-when-you-leave-an-android-app&#x2F;&quot;&gt;Code for detecting when you leave an Android app&lt;&#x2F;a&gt; for how to do it.]&lt;&#x2F;p&gt;
&lt;h3 id=&quot;press-the-home-button&quot;&gt;Press the home button&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts an Activity.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;home&quot; on the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onSaveInstanceState&lt;br&#x2F;&gt;onPause&lt;br&#x2F;&gt;onStop&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;&lt;pre&gt;onPause&lt;br&#x2F;&gt;onSaveInstanceState&lt;br&#x2F;&gt;onStop&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When home was pressed, onPause and onStop were called in that order, and onSaveInstanceState was always called before onStop. The app was still running after the test.&lt;&#x2F;p&gt;
&lt;p&gt;Note that onSaveInstanceState can happen either before or after onPause.&lt;&#x2F;p&gt;
&lt;p&gt;This is consistent with the information given on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;reference&#x2F;android&#x2F;app&#x2F;Activity.html#ActivityLifecycle&quot;&gt;Android Activity API docs&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting with Honeycomb, an application is not in the killable state until its onStop() has returned. This impacts when onSaveInstanceState(Bundle) may be called (it may be safely called after onPause() and allows and application to safely wait until onStop() to save persistent state.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;press-the-home-button-in-a-second-activity&quot;&gt;Press the home button in a second activity&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts Activity1.&lt;&#x2F;li&gt;
&lt;li&gt;Press a button in the activity that launches Activity2.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;home&quot; on the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Results are identical to &quot;Press the home button&quot; above, with Activity2 receiving all the calls, and Activity1 not receiving any.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;press-the-back-button-to-exit&quot;&gt;Press the back button to exit&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts an Activity.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;back&quot; on the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onPause&lt;br&#x2F;&gt;onStop&lt;br&#x2F;&gt;onDestroy&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;(same as above)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When back was pressed, onPause, onStop and onDestroy were called. The app process was still running after the test.&lt;&#x2F;p&gt;
&lt;p&gt;onSaveInstanceState was not called, because there is no current state to resume - when you re-enter you will be back at the beginning.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;start-the-app&quot;&gt;Start the app&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onCreate&lt;br&#x2F;&gt;onStart&lt;br&#x2F;&gt;onResume&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;(same as above)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When the app was started, onCreate, onStart and onResume were called in that order.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turn-off-with-the-power-button&quot;&gt;Turn off with the power button&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press the power button (turn off).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onSaveInstanceState&lt;br&#x2F;&gt;onPause&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;&lt;pre&gt;onPause&lt;br&#x2F;&gt;onSaveInstanceState&lt;br&#x2F;&gt;onStop&lt;br&#x2F;&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When the phone was suspended, onSaveInstanceState and onPause were called in different orders, and onStop was called in some cases.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;turn-on-with-the-power-button&quot;&gt;Turn on with the power button&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;li&gt;Press the power button (turn off).&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press the power button (turn on).&lt;&#x2F;li&gt;
&lt;li&gt;Unlock the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onResume&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;&lt;pre&gt;onRestart&lt;br&#x2F;&gt;onStart&lt;br&#x2F;&gt;onResume&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When the phone was resumed, onResume was always called, and onRestart and onStart were sometimes called.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;restart-the-app-after-pressing-home&quot;&gt;Restart the app after pressing home&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;home&quot; on the device.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onRestart&lt;br&#x2F;&gt;onStart&lt;br&#x2F;&gt;onResume&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;(same as above)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When the app was restarted after pressing home, onCreate was not called, but onRestart, onStart and onResume were (in that order).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;restart-the-app-after-pressing-back&quot;&gt;Restart the app after pressing back&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;back&quot; on the device.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;onCreate&lt;br&#x2F;&gt;onStart&lt;br&#x2F;&gt;onResume&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;(same as above)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;When the app was restarted after pressing back, onRestart was not called, but onCreate, onStart and onResume were (in that order).&lt;&#x2F;p&gt;
&lt;p&gt;This is consistent with the idea that pressing back effectively ends the app, but pressing home effectively pauses it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;launch-a-second-activity&quot;&gt;Launch a second activity&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts Activity1.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press a button in the activity that launches Activity2.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;Activity1.onSaveInstanceState&lt;br&#x2F;&gt;Activity1.onPause&lt;br&#x2F;&gt;Activity2.onCreate&lt;br&#x2F;&gt;Activity2.onStart&lt;br&#x2F;&gt;Activity2.onResume&lt;br&#x2F;&gt;Activity1.onStop&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;&lt;pre&gt;Activity1.onPause&lt;br&#x2F;&gt;Activity2.onCreate&lt;br&#x2F;&gt;Activity2.onStart&lt;br&#x2F;&gt;Activity2.onResume&lt;br&#x2F;&gt;Activity1.onSaveInstanceState&lt;br&#x2F;&gt;Activity1.onStop&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;The later version of Android called onSaveInstanceState much later in the process. This is consistent with the documentation for the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.android.com&#x2F;reference&#x2F;android&#x2F;app&#x2F;Activity.html#ActivityLifecycle&quot;&gt;Activity Lifecycle&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In both cases Activity1&#x27;s onPause was called before any methods were called on Activity2.&lt;&#x2F;p&gt;
&lt;p&gt;In both cases Activity1&#x27;s onStop was called after all methods were called on Activity2.&lt;&#x2F;p&gt;
&lt;p&gt;Activity2&#x27;s methods were called just as if it were the starting activity of a freshly-launched app (see &quot;Start the app&quot; above).&lt;&#x2F;p&gt;
&lt;p&gt;Activity1&#x27;s methods were called just as if the home button had been pressed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;launch-an-external-activity&quot;&gt;Launch an external activity&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts an Activity.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events (in our activity only).&lt;&#x2F;li&gt;
&lt;li&gt;Press a button in the activity that launches the Android web browser.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Results are identical to &quot;Press the home button&quot; above.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;go-back-from-one-activity-to-another&quot;&gt;Go back from one activity to another&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts Activity1.&lt;&#x2F;li&gt;
&lt;li&gt;Press a button in the activity that launches Activity2.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events.&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;back&quot; on the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTC Wildfire S  &lt;br&#x2F;&gt;Android 2.3.5 (API 10)&lt;&#x2F;th&gt;&lt;th&gt;&lt;pre&gt;Activity2.onPause&lt;br&#x2F;&gt;Activity1.onRestart&lt;br&#x2F;&gt;Activity1.onStart&lt;br&#x2F;&gt;Activity1.onResume&lt;br&#x2F;&gt;Activity2.onStop&lt;br&#x2F;&gt;Activity2.onDestroy&lt;br&#x2F;&gt;  &lt;&#x2F;pre&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Nexus One Emulator (x86)  &lt;br&#x2F;&gt;Android L (API 20)&lt;&#x2F;td&gt;&lt;td&gt;(same as above)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Activity2 sees the same calls as in &quot;Press the back button to exit&quot; (i.e. it is destroyed).&lt;&#x2F;p&gt;
&lt;p&gt;Activity1 see the same calls as in &quot;Restart the app after pressing home&quot; (i.e. it is resumed).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;go-back-from-an-external-activity-to-ours&quot;&gt;Go back from an external activity to ours&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Ensure the app process is stopped.&lt;&#x2F;li&gt;
&lt;li&gt;Start the app. It starts an Activity.&lt;&#x2F;li&gt;
&lt;li&gt;Press a button in the activity that launches the Android web browser.&lt;&#x2F;li&gt;
&lt;li&gt;Start recording events (in our activity only).&lt;&#x2F;li&gt;
&lt;li&gt;Press &quot;back&quot; on the device.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Results are identical to &quot;Restart the app after pressing home&quot; above.&lt;&#x2F;p&gt;
&lt;p&gt;See my next blog post for how to detect whether we are leaving the app, or just transitioning between activities: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;05&#x2F;detecting-whether-an-android-app-is-stopping-or-starting&#x2F;&quot;&gt;Detecting whether an Android app is stopping (or starting)&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Code for detecting when you leave an Android app | Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Order of Android Activity lifecycle events […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anchal Maheshwari
    &lt;span class=&quot;comment_date&quot;&gt;2018-07-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;wow…. very nice information. its helpfull in interview prepration. thanks alot.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-07-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Anchal Maheshwari, I’m glad it was helpful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Snake in Dart</title>
          <pubDate>Wed, 03 Jun 2015 22:06:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/06/03/snake-in-dart/</link>
          <guid>https://artificialworlds.net/blog/2015/06/03/snake-in-dart/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/06/03/snake-in-dart/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;02&#x2F;05&#x2F;snake-in-groovy&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;12&#x2F;snake-in-ruby&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;27&#x2F;snake-in-zx-spectrum-basic&#x2F;&quot;&gt;BASIC&lt;&#x2F;a&gt;, Dart, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;06&#x2F;10&#x2F;snake-in-python-3-qt-5&#x2F;&quot;&gt;Python3+Qt5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m writing the game Snake in lots of programming languages, for fun, and to try out new languages. This time, Dart, which is for people who love Java and wish they didn&#x27;t have to do JavaScript.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Dart: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1f812e1e-83bc-4c81-bfb6-f818d145dc1d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-dart&#x2F;snake-dart.html&quot;&gt;Snake in Dart&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Preventing Kupfer switching to existing windows</title>
          <pubDate>Fri, 15 May 2015 13:06:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/05/15/preventing-kupfer-switching-to-existing-windows/</link>
          <guid>https://artificialworlds.net/blog/2015/05/15/preventing-kupfer-switching-to-existing-windows/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/05/15/preventing-kupfer-switching-to-existing-windows/">&lt;p&gt;I am enjoying using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;engla.github.io&#x2F;kupfer&#x2F;&quot;&gt;Kupfer&lt;&#x2F;a&gt; but I don&#x27;t like the way it switches to an existing window of an application, instead of launching a new instance.&lt;&#x2F;p&gt;
&lt;p&gt;I should write a patch for a config option or similar, but for now, here are my notes on how I disabled the behaviour in the source code:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo sensible-editor &#x2F;usr&#x2F;share&#x2F;kupfer&#x2F;kupfer&#x2F;launch.py
&lt;&#x2F;pre&gt;
&lt;p&gt;I just commented out 3 lines inside the launch_application method (lines 70-72 for me):&lt;&#x2F;p&gt;
&lt;pre&gt;#       if activate and svc.application_is_running(app_id):
#               svc.application_to_front(app_id)
#               return True
&lt;&#x2F;pre&gt;
&lt;p&gt;This prevents Kupfer looking for open windows before launching.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using Kupfer on MATE</title>
          <pubDate>Tue, 12 May 2015 13:45:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/05/12/using-kupfer-on-mate/</link>
          <guid>https://artificialworlds.net/blog/2015/05/12/using-kupfer-on-mate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/05/12/using-kupfer-on-mate/">&lt;p&gt;I am trying out &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mate-desktop.org&#x2F;&quot;&gt;MATE&lt;&#x2F;a&gt; desktop and really liking it.&lt;&#x2F;p&gt;
&lt;p&gt;I like to use a (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;do.cooperteam.net&#x2F;&quot;&gt;GNOME-Do&lt;&#x2F;a&gt; style) keyboard-driven application launcher, and have found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;engla.github.io&#x2F;kupfer&#x2F;&quot;&gt;Kupfer&lt;&#x2F;a&gt; really good.&lt;&#x2F;p&gt;
&lt;p&gt;Kupfer as packaged for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ubuntu-mate.org&#x2F;&quot;&gt;Ubuntu MATE&lt;&#x2F;a&gt; does not support MATE desktop, so the file manager (&quot;Caja&quot;) does not show up by default, along with other things like the preferences dialogs, and there is no support for actions like Log Out or Shut Down.&lt;&#x2F;p&gt;
&lt;p&gt;To get MATE applications to show up in Kupfer, all I had to do was make up a config value that is not presented in the preferences, by editing the config file:&lt;&#x2F;p&gt;
&lt;pre&gt;pluma ~&#x2F;.config&#x2F;kupfer&#x2F;kupfer.cfg&lt;&#x2F;pre&gt;
&lt;p&gt;I found the line that started &quot;desktop_type = &quot; and changed it to say:&lt;&#x2F;p&gt;
&lt;pre&gt;desktop_type = MATE&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt; [Thanks to Kristian Nygaard Jensen and Igor Santos]: if you can&#x27;t find that line in the file, add this at the bottom of the file:&lt;&#x2F;p&gt;
&lt;pre&gt;[plugin_applications]
desktop_type = MATE
&lt;&#x2F;pre&gt;
&lt;p&gt;To get MATE session commands in Kupfer, I copied the GNOME session management plugin and made a similar one for MATE.&lt;&#x2F;p&gt;
&lt;pre&gt;cd &#x2F;usr&#x2F;share&#x2F;kupfer&#x2F;kupfer&#x2F;plugin
sudo cp session_gnome.py session_mate.py
sudo pluma session_mate.py
&lt;&#x2F;pre&gt;
&lt;p&gt;I just changed the 3 lines after &quot;# sequences of argument lists&quot;, and made them look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;LOGOUT_CMD = ([&quot;mate-session-save&quot;, &quot;--logout-dialog&quot;],)
SHUTDOWN_CMD = ([&quot;mate-session-save&quot;, &quot;--shutdown-dialog&quot;],)
LOCKSCREEN_CMD = ([&quot;mate-screensaver-command&quot;, &quot;--lock&quot;],)
&lt;&#x2F;pre&gt;
&lt;p&gt;When I restarted Kupfer I was able to open MATE applications and control the MATE session using Kupfer.&lt;&#x2F;p&gt;
&lt;p&gt;There is an open pull request from 2013 for Kupfer to add MATE session management: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;engla&#x2F;kupfer&#x2F;pull&#x2F;15&quot;&gt;github.com&#x2F;engla&#x2F;kupfer&#x2F;pull&#x2F;15&lt;&#x2F;a&gt; - thanks to its author &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;labero&quot;&gt;labero&lt;&#x2F;a&gt;, since this showed me what I needed to do for that part.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Kristian Nygaard Jensen
    &lt;span class=&quot;comment_date&quot;&gt;2015-05-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;in which section in the config file […] did you find desktop_type.&lt;br&#x2F;&gt;
My kupfer.cfg does not contain desktop_type&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Igor Santos
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Same question here… There’s no &lt;code&gt;desktop_type&lt;&#x2F;code&gt; field.&lt;br&#x2F;&gt;
In which section was it? I’ve thrown it in some places and it didn’t work out.&lt;&#x2F;p&gt;
&lt;p&gt;I’ve added a bunch of request in the project’s launchpad page asking for the maintainer to take a look at the bugs and PRs. If it’s possible, please, join there as well: &lt;a href=&quot;https:&#x2F;&#x2F;answers.launchpad.net&#x2F;kupfer&#x2F;+question&#x2F;219854&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;answers.launchpad.net&#x2F;kupfer&#x2F;+questio...&lt;&#x2F;a&gt;&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;kupfer&#x2F;+bug&#x2F;1463500&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;kupfer&#x2F;+bug&#x2F;1463500&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Igor Santos
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Actually, after some fiddling I found it: if the entry is not in the file, include this section:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;&lt;br&#x2F;&gt;
[plugin_applications]&lt;br&#x2F;&gt;
desktop_type = MATE&lt;br&#x2F;&gt;
&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This section can be created by Kupfer preferences if you change your default desktop from GNOME to something else on the &lt;code&gt;Applications&lt;&#x2F;code&gt; plugin.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Ah, thanks Igor.  That explains why it was there for me.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have updated the post – thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Tweetable Art Code</title>
          <pubDate>Mon, 04 May 2015 10:11:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/05/04/tweetable-art-code/</link>
          <guid>https://artificialworlds.net/blog/2015/05/04/tweetable-art-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/05/04/tweetable-art-code/">&lt;p&gt;Picking apart some cool code to draw pretty pictures with code that fits into 3 tweets:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Tweetable Art Code&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3c67807f-2377-4251-995f-c8529373b6c5&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;This comes from a codegolf question here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codegolf.stackexchange.com&#x2F;questions&#x2F;35569&#x2F;tweetable-mathematical-art&quot;&gt;Tweetable Mathematical Art&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;tweetable-art&#x2F;tweetable-art.html&quot;&gt;Tweetable Art Code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape 0.3.1 - now with zoom!</title>
          <pubDate>Sat, 02 May 2015 07:53:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/05/02/rabbit-escape-0-3-1-now-with-zoom/</link>
          <guid>https://artificialworlds.net/blog/2015/05/02/rabbit-escape-0-3-1-now-with-zoom/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/05/02/rabbit-escape-0-3-1-now-with-zoom/">&lt;p&gt;I&#x27;ve just release the latest version of Rabbit Escape, which makes things look a lot nicer because you can zoom in, getting you much closer to your rabbits:&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rabbitescape-android-zoomed.png&quot; alt=&quot;rabbitescape-android-zoomed&quot; width=&quot;320&quot; height=&quot;480&quot; class=&quot;alignnone size-full wp-image-1720&quot;&gt;
&lt;p&gt;There are still 60 levels of Lemmings and Pingus -like gameplay, all downloadable for free &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;artificialworlds.net&#x2F;rabbit-escape&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve also improved performance significantly, so you should notice things get smoother on older devices.&lt;&#x2F;p&gt;
&lt;p&gt;All those zoomed images increase the download size to 9MB, which is a pity, but that&#x27;s still pretty small.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Treat warnings as errors in a (Gnu) Makefile</title>
          <pubDate>Wed, 22 Apr 2015 14:36:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/04/22/treat-warnings-as-errors-in-a-gnu-makefile/</link>
          <guid>https://artificialworlds.net/blog/2015/04/22/treat-warnings-as-errors-in-a-gnu-makefile/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/04/22/treat-warnings-as-errors-in-a-gnu-makefile/">&lt;p&gt;I got hit again last night by a bug in my Makefile that meant I effectively ran rm -rf &#x2F;*, which was not fun.&lt;&#x2F;p&gt;
&lt;p&gt;Today I have been looking for how to make Make stop if a variable is undefined.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my solution. This Makefile contains a bug (&quot;SRCS&quot; instead of &quot;SRC&quot;):&lt;&#x2F;p&gt;
&lt;pre&gt;# Turn on the warning we want
MAKEFLAGS += --warn-undefined-variables

# Make sure MAKECMDGOALS is defined, so it doesn&#x27;t cause an error itself
ifndef MAKECMDGOALS
MAKECMDGOALS = all
endif

SRC=hello.c

all: compile

# Fails if the Makefile contains any warnings.
# Run this Makefile with the same goals, but with the -n flag.
# Grep for warnings, and fail if any are found.
no-make-warnings:
    ! make -n $(MAKECMDGOALS) 2&amp;gt;&amp;amp;1 &amp;gt;&#x2F;dev&#x2F;null | grep warning

# Targets you want to check must depend on no-make-warnings
compile: no-make-warnings
    gcc -o hello $(SRCS)
&lt;&#x2F;pre&gt;
&lt;p&gt;When I run it I get:&lt;&#x2F;p&gt;
&lt;pre&gt;$ make
! make -n all 2&amp;gt;&amp;amp;1 &amp;gt;&#x2F;dev&#x2F;null | grep warning
Makefile:17: warning: undefined variable `SRCS&#x27;
make: *** [no-make-warnings] Error 1
&lt;&#x2F;pre&gt;
&lt;p&gt;When I correct the bug I get:&lt;&#x2F;p&gt;
&lt;pre&gt;$ make
! make -n all 2&amp;gt;&amp;amp;1 &amp;gt;&#x2F;dev&#x2F;null | grep warning
gcc -o hello hello.c
&lt;&#x2F;pre&gt;
&lt;p&gt;As expected.&lt;&#x2F;p&gt;
&lt;p&gt;To make a target warnings-resistant, you have to make it depend on the target no-make-warnings. If anyone has suggestions for how to avoid needing this, please comment.&lt;&#x2F;p&gt;
&lt;p&gt;I also posted this as a StackOverflow answer: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;10859916&#x2F;how-to-treat-a-warning-as-an-error-in-a-makefile&#x2F;29800774#29800774&quot;&gt;How to treat a warning as an error in a Makefile?&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Switching Xfce to use metacity</title>
          <pubDate>Mon, 20 Apr 2015 16:01:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/04/20/switching-xfce-to-use-metacity/</link>
          <guid>https://artificialworlds.net/blog/2015/04/20/switching-xfce-to-use-metacity/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/04/20/switching-xfce-to-use-metacity/">&lt;p&gt;I am trying out Xfce and liking it. However, I&#x27;ve never found a window manager better than Metacity, so I&#x27;d like to use it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I switched:&lt;&#x2F;p&gt;
&lt;pre&gt;# Install it
sudo apt-get install metacity metacity-themes

# Tell it how many workspaces I want
gsettings set org.gnome.desktop.wm.preferences num-workspaces 9

# Of course, put my maximise, close buttons etc. in the right place
gsettings set org.gnome.desktop.wm.preferences button-layout &#x27;:minimize,maximize,close&#x27;

# See what themes are available
ls &#x2F;usr&#x2F;share&#x2F;themes&#x2F;

# Set the theme I want
gsettings set org.gnome.desktop.wm.preferences theme Dopple

# Try it out immediately
metacity --replace

# Make the change permanent, log out and edit
# ~&#x2F;.config&#x2F;xfce4&#x2F;xfconf&#x2F;xfce-perchannel-xml&#x2F;xfce4-session.xml
# and change this line:
#  &amp;lt;property name=&quot;Client0_Command&quot; type=&quot;empty&quot;&#x2F;&amp;gt;
# to this:
#  &amp;lt;property name=&quot;Client0_Command&quot; type=&quot;array&quot;&amp;gt;&amp;lt;value type=&quot;string&quot; value=&quot;metacity&quot;&#x2F;&amp;gt;&amp;lt;&#x2F;property&amp;gt;
&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    johnywhy
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;hi, what’s the difference? thx&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;The difference is a few subtle behaviours when you drag windows around, and keyboard shortcuts like Alt-F7 for moving windows that I am used to.  I use MATE now, which does everything I want very nicely.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Eric Marceau
    &lt;span class=&quot;comment_date&quot;&gt;2019-03-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Trying Metacity on Xubuntu 18.04.&lt;&#x2F;p&gt;
&lt;p&gt;Is anyone experiencing &quot;jitter&quot; and slow dragging when trying to drag a window around on the screen?&lt;&#x2F;p&gt;
&lt;p&gt;That is not experienced when using the XFCE on Xubuntu or straight Xubuntu.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Java game programming: image rendering hints make no difference to rendering time</title>
          <pubDate>Mon, 06 Apr 2015 22:50:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/04/06/java-game-programming-image-rendering-hints-make-no-difference-to-rendering-time/</link>
          <guid>https://artificialworlds.net/blog/2015/04/06/java-game-programming-image-rendering-hints-make-no-difference-to-rendering-time/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/04/06/java-game-programming-image-rendering-hints-make-no-difference-to-rendering-time/">&lt;p&gt;I am writing an Open Source&#x2F;Free Software Java desktop game (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;) and am experimenting with allowing you to zoom in so that the images being rendered are fairly big, and I&#x27;m seeing some slow-down.&lt;&#x2F;p&gt;
&lt;p&gt;[Note: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; is also available on Android, where it appears to have no problems with rendering speed...]&lt;&#x2F;p&gt;
&lt;p&gt;To speed it up, I have been experimenting with passing various values to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;7&#x2F;docs&#x2F;api&#x2F;java&#x2F;awt&#x2F;Graphics2D.html&quot;&gt;Graphics2D&lt;&#x2F;a&gt;&#x27;s &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;7&#x2F;docs&#x2F;api&#x2F;java&#x2F;awt&#x2F;Graphics2D.html#setRenderingHint%28java.awt.RenderingHints.Key,%20java.lang.Object%29&quot;&gt;setRenderingHint&lt;&#x2F;a&gt; method.&lt;&#x2F;p&gt;
&lt;p&gt;My conclusion is that on my platform (OpenJDK Java 7 on Linux) it makes no difference whatsoever. Please leave a comment if I&#x27;m doing it wrong.&lt;&#x2F;p&gt;
&lt;p&gt;I am timing how long each frame takes to render, and comparing the time when I insert this code before my rendering code:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; As slow and high-quality as possible, please!
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
g.setRenderingHint( RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY );
g.setRenderingHint( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY );
g.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY );
&lt;&#x2F;pre&gt;
&lt;p&gt;with inserting this before my rendering code:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; As fast and low-quality as possible, please!
g.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF );
g.setRenderingHint( RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED );
g.setRenderingHint( RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED );
g.setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED );
&lt;&#x2F;pre&gt;
&lt;p&gt;[Documentation, such as it is, is here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;7&#x2F;docs&#x2F;api&#x2F;java&#x2F;awt&#x2F;RenderingHints.html&quot;&gt;RenderingHints&lt;&#x2F;a&gt;.]&lt;&#x2F;p&gt;
&lt;p&gt;I see no appreciable difference in rendering time or image quality between these two setups, or when I leave out the calls to setRenderingHint completely.&lt;&#x2F;p&gt;
&lt;p&gt;Platform details:&lt;&#x2F;p&gt;
&lt;pre&gt;$ java -version
java version &quot;1.7.0_75&quot;
OpenJDK Runtime Environment (IcedTea 2.5.4) (7u75-2.5.4-1~trusty1)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Snake in ZX Spectrum BASIC</title>
          <pubDate>Fri, 27 Mar 2015 08:12:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/03/27/snake-in-zx-spectrum-basic/</link>
          <guid>https://artificialworlds.net/blog/2015/03/27/snake-in-zx-spectrum-basic/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/03/27/snake-in-zx-spectrum-basic/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;02&#x2F;05&#x2F;snake-in-groovy&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;12&#x2F;snake-in-ruby&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;, BASIC, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;03&#x2F;snake-in-dart&#x2F;&quot;&gt;Dart&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;06&#x2F;10&#x2F;snake-in-python-3-qt-5&#x2F;&quot;&gt;Python3+Qt5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m writing the game Snake in lots of programming languages, for fun, and to try out new languages. This time, the first language I ever learned:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;ZX Spectrum BASIC: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a2e5ce32-160d-463f-87f0-bca6309f44b9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-zx-spectrum-basic&#x2F;snake-zx-spectrum-basic.html&quot;&gt;Snake in ZX Spectrum BASIC&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>fetchmail complaining about GoDaddy SSL certificate</title>
          <pubDate>Thu, 26 Mar 2015 22:22:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/03/26/fetchmail-complaining-about-godaddy-ssl-certificate/</link>
          <guid>https://artificialworlds.net/blog/2015/03/26/fetchmail-complaining-about-godaddy-ssl-certificate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/03/26/fetchmail-complaining-about-godaddy-ssl-certificate/">&lt;p&gt;&lt;strong&gt;Update: I don&#x27;t think this fixed the problem&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I was getting this every time I ran fetchmail.&lt;&#x2F;p&gt;
&lt;pre&gt;fetchmail: Server certificate verification error: unable to get local issuer certificate
fetchmail: Broken certification chain at: &#x2F;C=US&#x2F;ST=Arizona&#x2F;L=Scottsdale&#x2F;O=GoDaddy.com, Inc.&#x2F;OU=http:&#x2F;&#x2F;certs.godaddy.com&#x2F;repository&#x2F;&#x2F;CN=Go Daddy Secure Certificate Authority - G2
fetchmail: This could mean that the server did not provide the intermediate CA&#x27;s certificate(s), which is nothing fetchmail could do anything about.  For details, please see the README.SSL-SERVER document that ships with fetchmail.
fetchmail: This could mean that the root CA&#x27;s signing certificate is not in the trusted CA certificate location, or that c_rehash needs to be run on the certificate directory. For details, please see the documentation of --sslcertpath and --sslcertfile in the manual page.
fetchmail: Server certificate verification error: certificate not trusted
fetchmail: Server certificate verification error: unable to verify the first certificate
fetchmail: Warning: the connection is insecure, continuing anyway. (Better use --sslcertck)
&lt;&#x2F;pre&gt;
&lt;p&gt;I appear to have fixed it by running:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo c_rehash
&lt;&#x2F;pre&gt;
&lt;p&gt;I found this by reading the documentation on --sslcertpath in the fetchmail man page. (As the error message told me to...)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Encapsulation as passing on responsibility</title>
          <pubDate>Mon, 16 Mar 2015 14:17:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/03/16/encapsulation-as-passing-on-responsibility/</link>
          <guid>https://artificialworlds.net/blog/2015/03/16/encapsulation-as-passing-on-responsibility/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/03/16/encapsulation-as-passing-on-responsibility/">&lt;p&gt;I recently dealt with some code that I felt was not properly encapsulated, but in a sense that I&#x27;ve not seen articulated in this way before. Please enlighten me if I missed it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a snippet:&lt;&#x2F;p&gt;
&lt;pre&gt;OutputThing manipulate( InputThing input )
{
    ProcessingThing proc( input );
    moreProcessing( proc.intermediateResult, input );
    return OutputThing( proc );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;This function takes in some input and returns some output, using a ProcessingThing to do some work.&lt;&#x2F;p&gt;
&lt;p&gt;I needed to make a change to ProcessingThing to support some functionality, and I fell foul of something I didn&#x27;t expect.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to transform my input a bit, and wrote that code inside ProcessingThing. What I didn&#x27;t expect was that the untransformed input would later be used (in the moreProcessing call). The moreProcessing code failed because the transformation I had done made the ProcessingThing inconsistent with the InputThing.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously, I should have checked more carefully.&lt;&#x2F;p&gt;
&lt;p&gt;But, it struck me that there is a pattern here. We are processing something by passing it from step to step in a &quot;production line&quot; of code. By re-using something from an older step, we violate this metaphor of passing on responsibility, because suddenly there are two copies of our input - it wasn&#x27;t passed on at all.&lt;&#x2F;p&gt;
&lt;p&gt;This feels a bit like we&#x27;ve violated the encapsulation of input by ProcessingThing, but maybe I&#x27;m stretching the word encapsulation too far?&lt;&#x2F;p&gt;
&lt;p&gt;I feel like if ProcessingThing properly encapsulated the details of InputThing, we wouldn&#x27;t need to re-use input later.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, the fix in my case was to put the moreProcessing logic into ProcessingThing, meaning there was no need to refer back to the original input. I think this supports the argument that we are talking about encapsulation.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Snake in Ruby</title>
          <pubDate>Thu, 12 Mar 2015 02:09:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/03/12/snake-in-ruby/</link>
          <guid>https://artificialworlds.net/blog/2015/03/12/snake-in-ruby/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/03/12/snake-in-ruby/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;02&#x2F;05&#x2F;snake-in-groovy&#x2F;&quot;&gt;Groovy&lt;&#x2F;a&gt;, Ruby, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;27&#x2F;snake-in-zx-spectrum-basic&#x2F;&quot;&gt;BASIC&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;03&#x2F;snake-in-dart&#x2F;&quot;&gt;Dart&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;06&#x2F;10&#x2F;snake-in-python-3-qt-5&#x2F;&quot;&gt;Python3+Qt5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m writing the game Snake in lots of programming languages, for fun, and to try out new languages.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Ruby: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;86bfd500-05e6-4aa4-af41-919b76430a9f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-ruby&#x2F;snake-ruby.html&quot;&gt;Snake in Ruby&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Batch-converting audio files to be louder (on Linux)</title>
          <pubDate>Sat, 28 Feb 2015 07:14:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/28/batch-converting-audio-files-to-be-louder-on-linux/</link>
          <guid>https://artificialworlds.net/blog/2015/02/28/batch-converting-audio-files-to-be-louder-on-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/28/batch-converting-audio-files-to-be-louder-on-linux/">&lt;p&gt;My mp3 player is very quiet, so I wanted to make all my podcasts as loud as possible.&lt;&#x2F;p&gt;
&lt;p&gt;First I ran this to get the programs I needed:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install libav-tools normalize-audio
&lt;&#x2F;pre&gt;
&lt;p&gt;To convert each file I made a script that makes a &quot;loud&quot; directory, and puts the loud version of a file inside there. It uses the normalize-audio command to do it.&lt;&#x2F;p&gt;
&lt;p&gt;Note that this script encodes your (now louder) podcasts into Ogg Vorbis format at 50kb&#x2F;s, which is quite low quality.&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

set -e
set -u

FILE=&quot;$1&quot;
DIR=`dirname &quot;$FILE&quot;`

FILENAME=`basename &quot;$FILE&quot;`
WAV_FILENAME=&quot;${FILENAME}.wav&quot;

LOUD_DIR=&quot;$DIR&#x2F;loud&quot;
WAV_FILE=&quot;$LOUD_DIR&#x2F;$WAV_FILENAME&quot;
LOUD_FILE=&quot;$LOUD_DIR&#x2F;$FILENAME&quot;

mkdir -p &quot;$LOUD_DIR&quot;

avconv -loglevel quiet -i &quot;$FILE&quot; &quot;$WAV_FILE&quot;
normalize-audio -q -a 1 &quot;$WAV_FILE&quot;
avconv -loglevel quiet -i &quot;$WAV_FILE&quot; -c:a libvorbis -b:a 50k &quot;$LOUD_FILE&quot;
rm &quot;$WAV_FILE&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;Finally I placed a Makefile in the directory containing podcasts directories, like this:&lt;&#x2F;p&gt;
&lt;pre&gt;MP3S := $(wildcard *.mp3)
LOUD_MP3S := $(MP3S:%.mp3=loud&#x2F;%.mp3)

OGGS := $(wildcard *.ogg)
LOUD_OGGS := $(OGGS:%.ogg=loud&#x2F;%.ogg)

all: $(LOUD_OGGS) $(LOUD_MP3S)

loud&#x2F;%.ogg: %.ogg
	loud &quot;$&amp;lt;&quot;

loud&#x2F;%.mp3: %.mp3
	loud &quot;$&amp;lt;&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can make loud versions of all podcasts by just cding into the directory containing the Makefile, and typing make. By the power of make, it only converts files that have not already been converted.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why Rabbit Escape is Open Source &#x2F; Free Software</title>
          <pubDate>Fri, 27 Feb 2015 08:00:32 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/27/why-rabbit-escape-is-open-source-free-software/</link>
          <guid>https://artificialworlds.net/blog/2015/02/27/why-rabbit-escape-is-open-source-free-software/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/27/why-rabbit-escape-is-open-source-free-software/">&lt;p&gt;Why I wanted to make Rabbit Escape Free Software, even though I also plan to sell it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Why Rabbit Escape is Open Source &#x2F; Free Software&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;443e9144-3b2c-4ed9-934e-278ee32ceaf9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Because I want to share it.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sebastian Silva
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for your game, I really enjoyed sharing it with my kids. Out of curiosity, how did your selling on Play go?&lt;br&#x2F;&gt;
Great game!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Sebastian, thanks!  Very few people have bought the paid version from the Play Store, and even fewer have downloaded the (identical) free version I recently released :–(&lt;&#x2F;p&gt;
&lt;p&gt;Possibly a failure of marketing…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sebastian Silva
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for your sincere answer! I thought Rabbit Escape is a very nice pre-programming game for kids (one is 6, but even my 3Â½ year old was enjoying it and succeeding in early levels).&lt;&#x2F;p&gt;
&lt;p&gt;I wish you success and inspiration and congratulate you on this beautiful game that I actively recommend to friends and fellow parents.&lt;&#x2F;p&gt;
&lt;p&gt;Regards from Peru&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Sebastian thank you so much for the encouragement!  I am very glad it has been fun for your kids – that is the real reason I wrote it, so I am satisfied :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>What is a good company?</title>
          <pubDate>Tue, 24 Feb 2015 09:45:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/24/what-is-a-good-company/</link>
          <guid>https://artificialworlds.net/blog/2015/02/24/what-is-a-good-company/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/24/what-is-a-good-company/">&lt;p&gt;I&#x27;ve been trying to work out what I think would be a good company to work for. Here&#x27;s what I&#x27;ve got so far. Please comment pointing out what I got wrong and missed out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;be-coo&quot;&gt;Be coo&lt;&#x2F;h2&gt;
&lt;p&gt;We believe a company should be a good place to work.&lt;&#x2F;p&gt;
&lt;p&gt;We sum that up in one rule:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;be coo&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;rules&quot;&gt;Rules&lt;&#x2F;h2&gt;
&lt;p&gt;We define being coo like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Value humans&lt;&#x2F;li&gt;
&lt;li&gt;Be small, and do not pursue growth as a goal&lt;&#x2F;li&gt;
&lt;li&gt;Be privately owned&lt;&#x2F;li&gt;
&lt;li&gt;Make something that people want&lt;&#x2F;li&gt;
&lt;li&gt;Get paid for what you do&lt;&#x2F;li&gt;
&lt;li&gt;Contribute to wider society&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;More detail:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;value-humans&quot;&gt;Value humans&lt;&#x2F;h2&gt;
&lt;p&gt;Be kind to people: all people, including your customers, your employees and your boss.&lt;&#x2F;p&gt;
&lt;p&gt;Use in-person communication whenever possible, and otherwise use live video chat. For talking about careers, performance etc. never settle for voice-only or text.&lt;&#x2F;p&gt;
&lt;p&gt;Regularly meet the people you manage and encourage them about what they are doing well. Rarely, talk to them about what didn&#x27;t go well. Sometimes, if someone is not able to do a role it is better for everyone if their role changes or they get another job. When it looks like this might be the situation, communicate clearly and honestly about it with them both before that decision is made and afterwards.&lt;&#x2F;p&gt;
&lt;p&gt;In performance reviews, gently reprimand people who regularly work more than their contracted hours.&lt;&#x2F;p&gt;
&lt;p&gt;Have a policy on acceptable behaviour, which is enforced by humans telling other humans what they did was not OK, when it wasn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;Do your best to employ people based on their ability to do the job. This includes not employing them because:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They look or sound right&lt;&#x2F;li&gt;
&lt;li&gt;They have spare time (e.g. don&#x27;t exclude someone because they don&#x27;t do any open source)&lt;&#x2F;li&gt;
&lt;li&gt;They seem like they are &quot;one of us&quot; or &quot;get it&quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Instead, look for evidence that they can do the job (this might include open source contributions, obviously), and that you can work with them. Being able to do the job will mean that they &quot;get&quot; things, and being able to work with them will mean that you can communicate effectively with them.&lt;&#x2F;p&gt;
&lt;p&gt;This is obviously hard. There are contradictions in the sentences above. Pro-actively invite feedback, accept negative feedback, and work to improve.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;be-small-and-do-not-pursue-growth-as-a-goal&quot;&gt;Be small, and do not pursue growth as a goal&lt;&#x2F;h2&gt;
&lt;p&gt;Growth is a stupid goal. Aim to do a good job. Aim to make enough money to pay your employees (and&#x2F;or yourself) well. Aim to be the right size to do a good job for your customers.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;be-privately-owned&quot;&gt;Be privately owned&lt;&#x2F;h2&gt;
&lt;p&gt;If you are owned by shareholders, you have a duty to prioritise shareholder value. This is in conflict with some of the rules, so don&#x27;t do it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-something-that-people-want&quot;&gt;Make something that people want&lt;&#x2F;h2&gt;
&lt;p&gt;You have to make something that people will buy or pay for, otherwise you won&#x27;t last long.&lt;&#x2F;p&gt;
&lt;p&gt;But, ask yourself whether people are glad that your company exists?&lt;&#x2F;p&gt;
&lt;p&gt;Make what you do something worthwhile: something that benefits people.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;get-paid-for-what-you-do&quot;&gt;Get paid for what you do&lt;&#x2F;h2&gt;
&lt;p&gt;If you don&#x27;t spend all day selling advertising, don&#x27;t get paid for selling advertising.&lt;&#x2F;p&gt;
&lt;p&gt;Find customers who want to pay you for the work you do. If your customers are actually benefitting from a concealed side-effect of what you do, the temptation to lie to them or fool them will be strong. (In reality, they are not your customers, and you won&#x27;t value them.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;contribute-to-wider-society&quot;&gt;Contribute to wider society&lt;&#x2F;h2&gt;
&lt;p&gt;Give people time off to do voluntary work. Within reason, don&#x27;t tell them what that can or can&#x27;t be.&lt;&#x2F;p&gt;
&lt;p&gt;Contribute bug reports, documentation and code to open source projects you use for your work.&lt;&#x2F;p&gt;
&lt;p&gt;Donate some of your profits to good causes supported by the staff.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    John Hutchinson
    &lt;span class=&quot;comment_date&quot;&gt;2015-03-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Really interesting article.  Do you mind if I circulate it in our company?&lt;br&#x2F;&gt;
(I got here after watching your videos on YT about Scheme, they are really good.  I’ve never really understood why you would bother with lambdas in C# but now I do!)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-03-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;John, thanks for the kind words – please do circulate it wherever you like.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>How to make your own levels for Rabbit Escape</title>
          <pubDate>Thu, 19 Feb 2015 23:42:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/19/how-to-make-your-own-levels-for-rabbit-escape/</link>
          <guid>https://artificialworlds.net/blog/2015/02/19/how-to-make-your-own-levels-for-rabbit-escape/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/19/how-to-make-your-own-levels-for-rabbit-escape/">&lt;p&gt;A little video showing you how to make levels for my new game &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;How to make your own levels for Rabbit Escape&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1aa087cd-0231-4a01-8945-c70c00b97055&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;There are more instructions on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;create-levels.html&quot;&gt;Creating levels for Rabbit Escape&lt;&#x2F;a&gt; page.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape v0.1 out now for Linux, Windows and Mac video</title>
          <pubDate>Sun, 15 Feb 2015 21:05:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/15/rabbit-escape-v0-1-out-now-for-linux-windows-and-mac/</link>
          <guid>https://artificialworlds.net/blog/2015/02/15/rabbit-escape-v0-1-out-now-for-linux-windows-and-mac/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/15/rabbit-escape-v0-1-out-now-for-linux-windows-and-mac/">&lt;p&gt;A little video to announce my new game, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Rabbit Escape v0.1 out now for Linux, Windows and Mac&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;8aeccbeb-25cc-4b5d-84b6-fd1e52351faf&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Download it, play it, enjoy it, and let me know how I can improve it!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Rabbit Escape (a bit like Lemmings) v0.1 released for Linux, Windows and Mac</title>
          <pubDate>Tue, 10 Feb 2015 11:27:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/10/rabbit-escape-a-bit-like-lemmings-v0-1-released-for-linux-windows-and-mac/</link>
          <guid>https://artificialworlds.net/blog/2015/02/10/rabbit-escape-a-bit-like-lemmings-v0-1-released-for-linux-windows-and-mac/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/10/rabbit-escape-a-bit-like-lemmings-v0-1-released-for-linux-windows-and-mac/">&lt;p&gt;Today I am releasing the first version of my new game, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;rabbit-escape&#x2F;&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s an arcade puzzle game inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Lemmings_%28video_game%29&quot;&gt;Lemmings&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;pingus.seul.org&#x2F;&quot;&gt;Pingus&lt;&#x2F;a&gt;, but intended to be simpler and easier to control on a mobile device.&lt;&#x2F;p&gt;
&lt;p&gt;Your task is to guide a party of rabbits from the entrance to the exit by dropping tokens in front of them that give them special abilities such as building bridges or climbing walls.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what it looks like:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rabbitescape-minilevel.gif&quot; alt=&quot;Rabbit Escape&quot; width=&quot;368&quot; height=&quot;306&quot; class=&quot;alignnone size-full wp-image-1639&quot;&gt;
&lt;p&gt;An Android version will be coming soon, but for now I&#x27;m releasing the desktop version for Linux, Windows and Mac.&lt;&#x2F;p&gt;
&lt;p&gt;There are 60 levels, and I&#x27;m hoping people will be sending me lots more soon!&lt;&#x2F;p&gt;
&lt;p&gt;I plan to write a level editor, but for now you can &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;rabbit-escape&#x2F;create-levels.html&quot;&gt;create your own levels&lt;&#x2F;a&gt; by editing text files.&lt;&#x2F;p&gt;
&lt;p&gt;The game is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fsf.org&#x2F;about&#x2F;what-is-free-software&quot;&gt;Free Software&lt;&#x2F;a&gt; under GPL v2, and the graphics, levels etc. are released under the non-commercial Creative Commons BY-NC-SA licence. The choice of a non-commercial license for these parts is intended to prevent people copying the game wholesale onto an app store and making money from it. If you want to use it under a different license, please contact me.&lt;&#x2F;p&gt;
&lt;p&gt;I plan to charge the minimum price on the Android store, and offer the desktop version for free (providing an opportunity for donations).&lt;&#x2F;p&gt;
&lt;p&gt;Please try it out and let me know how you get on. It&#x27;s a bit rough around the edges, but the game mechanics work, and it seems like it might be fun.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to contribute, I&#x27;d be very excited! You can find the code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;rabbit-escape&quot;&gt;github.com&#x2F;andybalaam&#x2F;rabbit-escape&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Snake in Groovy</title>
          <pubDate>Thu, 05 Feb 2015 17:51:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/05/snake-in-groovy/</link>
          <guid>https://artificialworlds.net/blog/2015/02/05/snake-in-groovy/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/05/snake-in-groovy/">&lt;p&gt;Series: Groovy, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;12&#x2F;snake-in-ruby&#x2F;&quot;&gt;Ruby&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;03&#x2F;27&#x2F;snake-in-zx-spectrum-basic&#x2F;&quot;&gt;BASIC&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2015&#x2F;06&#x2F;03&#x2F;snake-in-dart&#x2F;&quot;&gt;Dart&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;01&#x2F;07&#x2F;snake-in-elm&#x2F;&quot;&gt;Elm&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2016&#x2F;06&#x2F;10&#x2F;snake-in-python-3-qt-5&#x2F;&quot;&gt;Python3+Qt5&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m starting a series where I write the game Snake in lots of programming languages.&lt;&#x2F;p&gt;
&lt;p&gt;I almost always use writing Snake as my way in to understand a new language, so I&#x27;ll share my thoughts about each language as I go.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Groovy: First impressions (formed by writing Snake)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e7b892a2-b29e-4eb5-986a-2b3aaab65cd3&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;snake-groovy&#x2F;snake-groovy.html&quot;&gt;Snake in Groovy&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to, you can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.patreon.com&#x2F;andybalaam&quot;&gt;Support me on Patreon&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Programmatic equivalents of web.xml sections for Tomcat</title>
          <pubDate>Thu, 05 Feb 2015 12:48:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/02/05/programmatic-equivalents-of-web-xml-sections-for-tomcat/</link>
          <guid>https://artificialworlds.net/blog/2015/02/05/programmatic-equivalents-of-web-xml-sections-for-tomcat/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/02/05/programmatic-equivalents-of-web-xml-sections-for-tomcat/">&lt;p&gt;Most documentation for J2EE configuration is based on having a web.xml file, but I want to configure my Tomcat programmatically. Here are some of the things I have found out.&lt;&#x2F;p&gt;
&lt;p&gt;Please use the comments below to correct what I got wrong, and mention equivalents for other parts of web.xml.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;getting-started&quot;&gt;Getting started&lt;&#x2F;h2&gt;
&lt;pre&gt;&amp;lt;web-app ...
&lt;&#x2F;pre&gt;
&lt;p&gt;in code becomes something like:&lt;&#x2F;p&gt;
&lt;pre&gt;import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
...
Tomcat tomcat = new Tomcat();
Context context = tomcat.addContext( &quot;&quot;, &quot;WebContent&quot; );
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;adding-a-servlet&quot;&gt;Adding a Servlet&lt;&#x2F;h2&gt;
&lt;pre&gt;&amp;lt;web-app ...
    &amp;lt;servlet&amp;gt;
        ...
        &amp;lt;servlet-name&amp;gt;MyServlet&amp;lt;&#x2F;servlet-name&amp;gt;
        &amp;lt;servlet-class&amp;gt;com.example.MyServlet&amp;lt;&#x2F;servlet-class&amp;gt;
        ...
    &amp;lt;&#x2F;servlet&amp;gt;
    &amp;lt;servlet-mapping&amp;gt;
        &amp;lt;servlet-name&amp;gt;MyServlet&amp;lt;&#x2F;servlet-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;&#x2F;foo&#x2F;*&amp;lt;&#x2F;url-pattern&amp;gt;
    &amp;lt;&#x2F;servlet-mapping&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;in code becomes something like:&lt;&#x2F;p&gt;
&lt;pre&gt;...
Class&lt;!--?--&gt; servletClass = MyServlet.class;
&#x2F;&#x2F; MyServlet extends javax.servlet.http.HttpServlet
String servletName = servletClass.getSimpleName(); &#x2F;&#x2F; Or something else if you like
Tomcat.addServlet( context, servletName, servletClass.getName() );
context.addServletMapping( &quot;&#x2F;foo&#x2F;*&quot;, servletName );
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;adding-a-filter&quot;&gt;Adding a filter&lt;&#x2F;h2&gt;
&lt;pre&gt;&amp;lt;web-app ...
    &amp;lt;filter&amp;gt;
        &amp;lt;filter-name&amp;gt;MyFilter&amp;lt;&#x2F;filter-name&amp;gt;
        &amp;lt;filter-class&amp;gt;com.example.MyFilter&amp;lt;&#x2F;filter-class&amp;gt;
    &amp;lt;&#x2F;filter&amp;gt;
    &amp;lt;filter-mapping&amp;gt;
        &amp;lt;filter-name&amp;gt;MyFilter&amp;lt;&#x2F;filter-name&amp;gt;
        &amp;lt;url-pattern&amp;gt;&#x2F;bar&#x2F;*&amp;lt;&#x2F;url-pattern&amp;gt;
    &amp;lt;&#x2F;filter-mapping&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;in code becomes something like:&lt;&#x2F;p&gt;
&lt;pre&gt;...
import org.apache.catalina.deploy.FilterDef;
import org.apache.catalina.deploy.FilterMap;
...
Class&lt;!--?--&gt; filterClass = MyFilter.class;
&#x2F;&#x2F; MyFilter implements javax.servlet.Filter
String filterName = filterClass.getSimpleName(); &#x2F;&#x2F; Or something else if you like
FilterDef def = new FilterDef();
def.setFilterName( filterName );
context.addFilterDef( def );
FilterMap map = new FilterMap();
map.setFilterName( filterName );
map.addURLPattern( &quot;&#x2F;bar&#x2F;*&quot; );
context.addFilterMap( filterMap );
&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;adding-a-listener&quot;&gt;Adding a Listener&lt;&#x2F;h2&gt;
&lt;pre&gt;&amp;lt;web-app ...
    &amp;lt;listener&amp;gt;
    	&amp;lt;listener-class&amp;gt;com.example.MyContextListener&amp;lt;&#x2F;listener-class&amp;gt;
    &amp;lt;&#x2F;listener&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;in code becomes something like:&lt;&#x2F;p&gt;
&lt;pre&gt;...
context.addApplicationListener( MyContextListener.class.getName() );
&#x2F;&#x2F; MyContextListener implements javax.servlet.ServletContextListener
&lt;&#x2F;pre&gt;
&lt;p&gt;This is for a ServletContextListener: it may be similar for other listeners, but I&#x27;m not sure.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Terence Bandoian
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’d like to add that Context.addApplicationListener also appears to work for ServletRequestListener implementations.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for posting!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Terence!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Gustavo Castillo
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You have a typo in the first example.&lt;&#x2F;p&gt;
&lt;p&gt;Tomcat tomcat = new Tomcat();&lt;br&#x2F;&gt;
Context context = m_tomcat.addContext( &quot;&quot;, &quot;WebContent&quot; );&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-08-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Gustavo – fixed.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>JAX-RS (using Apache CXF) in embedded Tomcat example</title>
          <pubDate>Tue, 13 Jan 2015 17:24:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/01/13/jax-rs-using-apache-cxf-in-embedded-tomcat-example/</link>
          <guid>https://artificialworlds.net/blog/2015/01/13/jax-rs-using-apache-cxf-in-embedded-tomcat-example/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/01/13/jax-rs-using-apache-cxf-in-embedded-tomcat-example/">&lt;p&gt;I had serious trouble today firing up an embedded Tomcat server that serves up REST resources using JAX-RS via Apache CXF.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s minimal example, hopefully saving you the same trouble:&lt;&#x2F;p&gt;
&lt;p&gt;src&#x2F;HelloJaxRs.java:&lt;&#x2F;p&gt;
&lt;pre&gt;import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;

public class HelloJaxRs
{
    public static void main( String[] args ) throws Exception
    {
        Tomcat tomcat = new Tomcat();
        Context context = tomcat.addContext( &quot;&#x2F;&quot;, &quot;&quot; );

        Wrapper servlet = context.createWrapper();
        servlet.setName( &quot;jaxrs&quot; );
        servlet.setServletClass(
            &quot;org.apache.cxf.jaxrs.servlet.CXFNonSpringJaxrsServlet&quot; );

        servlet.addInitParameter(
            &quot;jaxrs.serviceClasses&quot;,
            Hello.class.getName()
        );

        servlet.setLoadOnStartup( 1 );
        context.addChild( servlet );
        context.addServletMapping( &quot;&#x2F;rest&#x2F;*&quot;, &quot;jaxrs&quot; );

        tomcat.start();
        tomcat.getServer().await();
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;src&#x2F;Hello.java:&lt;&#x2F;p&gt;
&lt;pre&gt;import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;

@Path( &quot;&#x2F;hello&#x2F;{name}&quot; )
public class Hello
{
    @GET
    public String getThing( @PathParam( &quot;name&quot; ) String name )
    {
        return &quot;Hello, &quot; + name;
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;build.xml:&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project default=&quot;run&quot;&amp;gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;lt;target name=&amp;quot;run&amp;quot; depends=&amp;quot;compile&amp;quot;&amp;amp;gt;
    &amp;amp;lt;mkdir dir=&amp;quot;bin&#x2F;tomcat.8080&#x2F;webapps&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;java
        classname=&amp;quot;HelloJaxRs&amp;quot;
        fork=&amp;quot;true&amp;quot;
        failonerror=&amp;quot;true&amp;quot;
        dir=&amp;quot;bin&amp;quot;
    &amp;amp;gt;
        &amp;amp;lt;classpath&amp;amp;gt;
            &amp;amp;lt;pathelement location=&amp;quot;bin&#x2F;&amp;quot;&#x2F;&amp;amp;gt;
            &amp;amp;lt;fileset dir=&amp;quot;lib&amp;quot;&amp;amp;gt;&amp;amp;lt;include name=&amp;quot;**&#x2F;*.jar&amp;quot;&#x2F;&amp;amp;gt;&amp;amp;lt;&#x2F;fileset&amp;amp;gt;
        &amp;amp;lt;&#x2F;classpath&amp;amp;gt;
    &amp;amp;lt;&#x2F;java&amp;amp;gt;
&amp;amp;lt;&#x2F;target&amp;amp;gt;

&amp;amp;lt;target name=&amp;quot;compile&amp;quot;&amp;amp;gt;
    &amp;amp;lt;mkdir dir=&amp;quot;bin&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;javac srcdir=&amp;quot;src&amp;quot; destdir=&amp;quot;bin&amp;quot;&amp;amp;gt;
        &amp;amp;lt;classpath&amp;amp;gt;
            &amp;amp;lt;fileset dir=&amp;quot;lib&amp;quot;&amp;amp;gt;&amp;amp;lt;include name=&amp;quot;**&#x2F;*.jar&amp;quot;&#x2F;&amp;amp;gt;&amp;amp;lt;&#x2F;fileset&amp;amp;gt;
        &amp;amp;lt;&#x2F;classpath&amp;amp;gt;
        &amp;amp;lt;compilerarg value=&amp;quot;-Werror&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;&#x2F;javac&amp;amp;gt;
&amp;amp;lt;&#x2F;target&amp;amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To download the dependencies (or you can do something clever with Maven):&lt;pre&gt;$ mkdir lib
$ cd lib
$ wget http:&#x2F;&#x2F;www.webhostingjams.com&#x2F;mirror&#x2F;apache&#x2F;tomcat&#x2F;tomcat-7&#x2F;v7.0.57&#x2F;bin&#x2F;apache-tomcat-7.0.57.tar.gz
$ tar -xzf apache-tomcat-7.0.57.tar.gz
$ wget http:&#x2F;&#x2F;www.apache.org&#x2F;dyn&#x2F;closer.cgi?path=&#x2F;cxf&#x2F;3.0.3&#x2F;apache-cxf-3.0.3.tar.gz
$ tar -xzf apache-cxf-3.0.3.tar.gz
$ cd ..
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To run Tomcat:&lt;pre&gt;$ ant
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then, in another terminal, you can see it&#x27;s working:&lt;pre&gt;$ curl http:&#x2F;&#x2F;localhost:8080&#x2F;rest&#x2F;hello&#x2F;andy
Hello, andy
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Vim persistent buffer list combined with saved sessions</title>
          <pubDate>Thu, 08 Jan 2015 11:47:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2015/01/08/vim-persistent-buffer-list-combined-with-saved-sessions/</link>
          <guid>https://artificialworlds.net/blog/2015/01/08/vim-persistent-buffer-list-combined-with-saved-sessions/</guid>
          <description xml:base="https://artificialworlds.net/blog/2015/01/08/vim-persistent-buffer-list-combined-with-saved-sessions/">&lt;p&gt;I like to use several saved &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.vim.org&#x2F;&quot;&gt;Vim&lt;&#x2F;a&gt; sessions to remember what files I was working on in a particular project.&lt;&#x2F;p&gt;
&lt;p&gt;I also like to have a list of buffers I have open on the left-hand side of the screen that is always visible and up-to-date.&lt;&#x2F;p&gt;
&lt;p&gt;Sometimes, the existence of a generated buffer like the list of buffers can confuse the session restore code, but I have found a way to make this work nicely using the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.vim.org&#x2F;scripts&#x2F;script.php?script_id=3619&quot;&gt;Buffergator&lt;&#x2F;a&gt; plugin and the built-in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;vimdoc.sourceforge.net&#x2F;htmldoc&#x2F;starting.html#:mksession&quot;&gt;mksession&lt;&#x2F;a&gt; command.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the relevant snippets from my .vimrc.&lt;&#x2F;p&gt;
&lt;p&gt;I am a beginner: please comment if you see bugs or possible improvements.&lt;&#x2F;p&gt;
&lt;pre&gt;&quot; Prerequisites: install the Buffergator plugin

&quot; Save session on exit, excluding problematic stuff like options

set sessionoptions=blank,buffers,curdir,tabpages,resize,winpos,winsize
au VimLeavePre * if v:this_session != &#x27;&#x27; | exec &quot;mks! &quot; . v:this_session | endif

&quot; Open Buffergator when you press F5

nnoremap &amp;lt;F5&amp;gt; :BuffergatorOpen&amp;lt;CR&amp;gt;

&quot; Various Buffergator options to make it persistent,
&quot; and displayed how I like

let g:buffergator_autodismiss_on_select = 0
let g:buffergator_display_regime = &quot;bufname&quot;
let g:buffergator_sort_regime    = &quot;filepath&quot;
let g:buffergator_autoupdate = 1

&quot; Suppress the error message about the Buffergator buffer
&quot; when you restore a session

function! OpenBuffergatorIfItsBufferExists()
    if bufnr(&quot;[[buffergator-buffers]]&quot;) &amp;gt; &#x27;&#x27;
        BuffergatorOpen
        normal o
    endif
endfunction

autocmd VimEnter * call OpenBuffergatorIfItsBufferExists()
&lt;&#x2F;pre&gt;
&lt;p&gt;Now when I start vim with vim -S path&#x2F;to&#x2F;session.vim the session is restored and saved when I exit, with a persistent buffer list on the left, and no errors during the session restore.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Val
    &lt;span class=&quot;comment_date&quot;&gt;2020-02-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;unfortunately, this did not work for me. BufferGator still complained (&quot;buffergator: only one buffer available&quot;) after having restored the session.&lt;&#x2F;p&gt;
&lt;p&gt;To make BufferGator happy after a session restore (performed using &quot;:source Session.vim&quot;), I’ve added the function below to my vimrc. The trick is to automatically call &quot;bnext&quot; as many times as available buffers. The snippet below also defines the command &quot;LoadSession&quot; while can load any saved session (ex: &quot;LoadSession projectA.vim&quot;). The keyboard shortcut &quot;l&quot; loads the default session (equivalent to LoadSession Session.vim)&lt;br&#x2F;&gt;
Best.&lt;&#x2F;p&gt;
&lt;p&gt;function! FuncLoadSession(name)&lt;br&#x2F;&gt;
exe &quot;source &quot; . a:name&lt;br&#x2F;&gt;
let i = len(getbufinfo({‘buflisted’:1}))&lt;br&#x2F;&gt;
let j = 0&lt;br&#x2F;&gt;
while j &amp;lt;= 1&lt;br&#x2F;&gt;
bnext&lt;br&#x2F;&gt;
let j+=1&lt;br&#x2F;&gt;
endwhile&lt;br&#x2F;&gt;
endfunction&lt;&#x2F;p&gt;
&lt;p&gt;command! -nargs=1 LoadSession call FuncLoadSession()&lt;br&#x2F;&gt;
map l :call FuncLoadSession(&quot;Session.vim&quot;)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Val
    &lt;span class=&quot;comment_date&quot;&gt;2020-02-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;so the angle brackets of my previous comment were removed automatically. Let’s try one more time using html character codes this time.&lt;&#x2F;p&gt;
&lt;p&gt;The last line, which defines the keyboard shortcut â€œlâ€,  was supposed to be:&lt;&#x2F;p&gt;
&lt;p&gt;map l :call FuncLoadSession(â€œSession.vimâ€)&lt;br&#x2F;&gt;
&amp;lt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Snowflake Christmas card web page on the Raspberry Pi</title>
          <pubDate>Tue, 16 Dec 2014 02:39:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-web-page-on-the-raspberry-pi/</link>
          <guid>https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-web-page-on-the-raspberry-pi/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-web-page-on-the-raspberry-pi/">&lt;p&gt;In this video I will show you how to make an electronic Christmas card for your friends or family using HTML and JavaScript, which means it will be a little web site that anyone can see by going to it in their Internet browser.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m doing this on the Raspberry Pi, but you can do the same thing on almost any computer that exists. All you need is a web browser (like Firefox or Internet Explorer) and a text editor (like Notepad or Gedit).&lt;&#x2F;p&gt;
&lt;p&gt;If this looks very difficult, try the video I made making a similar card using Scratch, which is a lot easier: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;12&#x2F;16&#x2F;snowflake-christmas-card-in-scratch-on-the-raspberry-pi&#x2F;&quot;&gt;Snowflake Christmas card in Scratch on the Raspberry Pi&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to use the snowflake picture I drew, right-click this link and choose &quot;Save link as...&quot; or similar: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;snowflake.svg&quot;&gt;snowflake.svg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Snowflake Christmas card web page on the Raspberry Pi&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;919db11e-3ac4-4daa-a338-4fed1da00769&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;If you&#x27;d like to compare my code against yours, right-click this link and choose &quot;Save link as...&quot; or similar: &lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;snowflakes.html&quot;&gt;snowflakes.html&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to see what the finished product looks like, just left-click on snowflakes.html above, instead of right-clicking.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Snowflake Christmas card in Scratch on the Raspberry Pi</title>
          <pubDate>Tue, 16 Dec 2014 02:27:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-in-scratch-on-the-raspberry-pi/</link>
          <guid>https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-in-scratch-on-the-raspberry-pi/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/12/16/snowflake-christmas-card-in-scratch-on-the-raspberry-pi/">&lt;p&gt;In this video I will show you how to make an electronic Christmas card for your friends or family using Scratch.&lt;&#x2F;p&gt;
&lt;p&gt;Scratch can work on most computers - you can download it from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;scratch.mit.edu&#x2F;&quot;&gt;http:&#x2F;&#x2F;scratch.mit.edu&#x2F;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Scratch is already installed on your Raspberry Pi if you&#x27;ve got &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.raspbian.org&#x2F;&quot;&gt;Raspian&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.raspberrypi.org&#x2F;introducing-noobs&#x2F;&quot;&gt;NOOBs&lt;&#x2F;a&gt; on your SD card.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Snowflake Christmas card in Scratch on the Raspberry Pi&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;bf12f717-5fd5-4488-9be7-f206fa65bccd&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;(If you want to get a Pi try my &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;Raspberry Pi: Before we start&lt;&#x2F;a&gt; video.)&lt;&#x2F;p&gt;
&lt;p&gt;If you want to try something more advanced, you can make a similar card as a web page: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;12&#x2F;16&#x2F;snowflake-christmas-card-web-page-on-the-raspberry-pi&#x2F;&quot;&gt;Snowflake Christmas card web page on the Raspberry Pi&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Encoding URLs in Java</title>
          <pubDate>Thu, 04 Dec 2014 13:05:12 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/12/04/encoding-urls-in-java/</link>
          <guid>https://artificialworlds.net/blog/2014/12/04/encoding-urls-in-java/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/12/04/encoding-urls-in-java/">&lt;p&gt;To encode a URL in Java, create a new instance of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;7&#x2F;docs&#x2F;api&#x2F;java&#x2F;net&#x2F;URI.html&quot;&gt;java.net.URI&lt;&#x2F;a&gt; and then call toString() or toASCIIString() on it.&lt;&#x2F;p&gt;
&lt;p&gt;DO NOT use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;7&#x2F;docs&#x2F;api&#x2F;java&#x2F;net&#x2F;URLEncoder.html&quot;&gt;java.net.URLEncoder&lt;&#x2F;a&gt;. It is for HTML form encoding, not encoding URLs, despite its name.&lt;&#x2F;p&gt;
&lt;p&gt;Each part of a URL must be escaped differently, so use the multi-argument constructors for URI where you have multiple parts you want to stick together. You can pass in null for arguments you want to omit, and it works the way you&#x27;d hope.&lt;&#x2F;p&gt;
&lt;p&gt;This program:&lt;&#x2F;p&gt;
&lt;pre&gt;class EncodeUrls
{
    public static void main( String[] args ) throws java.net.URISyntaxException
    {
        url( &quot;http&quot;, &quot;example.com&quot;, &quot;&#x2F;x&#x2F;y&#x2F;x&quot;, &quot;x=y&quot;, &quot;pos&quot; );

        url(
            &quot;http&quot;,
            &quot;exa\uD83D\uDCA9mple.com&quot;,
            &quot;&#x2F;x&#x2F;\uD83D\uDCA9&#x2F;x&quot;,
            &quot;x=\uD83D\uDCA9&quot;,
            &quot;po\uD83D\uDCA9&quot;
        );
    }

    private static void url(
        String scheme,
        String host,
        String path,
        String query,
        String fragment
    )
    throws java.net.URISyntaxException
    {
        java.net.URI uri = new java.net.URI(
            scheme, host, path, query, fragment );

        System.out.println( &quot;.&quot; + uri.toString() );
        System.out.println( &quot;.&quot; + uri.toASCIIString() );
        System.out.println();
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;prints:&lt;&#x2F;p&gt;
&lt;pre&gt;.http:&#x2F;&#x2F;example.com&#x2F;x&#x2F;y&#x2F;x?x=y#pos
.http:&#x2F;&#x2F;example.com&#x2F;x&#x2F;y&#x2F;x?x=y#pos

.http:&#x2F;&#x2F;exaðŸ’©mple.com&#x2F;x&#x2F;ðŸ’©&#x2F;x?x=ðŸ’©#poðŸ’©
.http:&#x2F;&#x2F;exa%F0%9F%92%A9mple.com&#x2F;x&#x2F;%F0%9F%92%A9&#x2F;x?x=%F0%9F%92%A9#po%F0%9F%92%A9
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Books that changed my life</title>
          <pubDate>Tue, 02 Dec 2014 11:56:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/12/02/books-that-changed-my-life/</link>
          <guid>https://artificialworlds.net/blog/2014/12/02/books-that-changed-my-life/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/12/02/books-that-changed-my-life/">&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Further Programming for the ZX Spectrum&lt;&#x2F;strong&gt; by Ian Stewart, Robin Jones&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Foundation&lt;&#x2F;strong&gt; by Isaac Asimov&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Crime and Punishment&lt;&#x2F;strong&gt; by Fyodor Dostoyevsky&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Structure and Interpretation of Computer Programs&lt;&#x2F;strong&gt; by Harold Abelson, Gerald Jay Sussman, Julie Sussman&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Slaughterhouse 5&lt;&#x2F;strong&gt; by Kurt Vonnegut&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Possibly: &lt;strong&gt;Children of Men&lt;&#x2F;strong&gt; by P.D. James (too early to say)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Why I use Linux</title>
          <pubDate>Mon, 01 Dec 2014 09:52:14 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/12/01/why-i-use-linux/</link>
          <guid>https://artificialworlds.net/blog/2014/12/01/why-i-use-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/12/01/why-i-use-linux/">&lt;p&gt;I have used Linux at home for quite a few years, and a couple of years ago I changed my work machine from Windows to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ubuntu.com&quot;&gt;Ubuntu Linux&lt;&#x2F;a&gt;. It&#x27;s made life at work better for me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;better-for-me&quot;&gt;Better for me&lt;&#x2F;h2&gt;
&lt;p&gt;This move has made me much more productive, saving valuable time and money. Here is why:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;security-updates-happen-automatically-without-hitches-and-rarely-requiring-reboots&quot;&gt;Security updates happen automatically, without hitches, and rarely requiring reboots.&lt;&#x2F;h3&gt;
&lt;p&gt;Since I changed, security updates are applied almost as soon as they are made available with no interaction from me. I have never seen a security upgrade that broke my system and prevented me working, whereas before I saw this several times.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-computer-works-faster&quot;&gt;My computer works faster.&lt;&#x2F;h3&gt;
&lt;p&gt;My computer feels much faster to work with. In recent tests for our project, we found that the time spent waiting for a compile on my Linux machine was half that spent waiting on an identical system using Windows.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-work-faster&quot;&gt;I work faster.&lt;&#x2F;h3&gt;
&lt;p&gt;Because my system is more customisable than before, I can work the way that feels more comfortable to me. Support for keyboard shortcuts is more comprehensive in Linux, and I can work using the keyboard for most things, which helps prevent recurrence of RSI, from which I have suffered in the past.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-can-automate-repetitive-tasks&quot;&gt;I can automate repetitive tasks.&lt;&#x2F;h3&gt;
&lt;p&gt;It&#x27;s much easier to write small scripts on Linux for things that I do every day. This means I spend more time thinking about the work I have to do, instead of all the button-clicking required to do it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-can-have-more-programs-open&quot;&gt;I can have more programs open.&lt;&#x2F;h3&gt;
&lt;p&gt;My computer now works comfortably simultaneously running 3 or 4 heavy corporate applications, at least 2 copies of Eclipse, Firefox with many tabs open, several Chrome windows, 3 text editor windows, a 1GB virtual machine and many other programs, without slowing down or crashing. When I used Windows I needed fewer programs open, but because Linux manages memory better and many of the programs are less memory-hungry, I can do more things at the same time.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-can-use-virtual-desktops&quot;&gt;I can use &quot;virtual desktops&quot;.&lt;&#x2F;h3&gt;
&lt;p&gt;It&#x27;s hard to explain why until you&#x27;ve tried it, but virtual desktops are an incredibly addictive feature. I avoid neck strain by using a single monitor directly in front of me*, but I am able to have lots of separate work spaces and can switch very quickly between them. I use one desktop for email and instant messaging, another for the Internet, and several more for programming in different environments. Switching between them happens as fast as I can press the relevant keyboard shortcut. This is a killer feature of modern Linux desktops in my opinion.&lt;&#x2F;p&gt;
&lt;p&gt;[*Multiple-monitor setups are well-supported too, of course.]&lt;&#x2F;p&gt;
&lt;h3 id=&quot;modern-tools-work-better&quot;&gt;Modern tools work better.&lt;&#x2F;h3&gt;
&lt;p&gt;Modern software - especially programming software - is frequently developed on Linux, and has its &quot;home&quot; there, meaning it is easier to install and update than on other platforms. Examples include: Python, Node.js, Perl, Vim, Emacs, Bash and Ruby, all of which are much easier to use and install on Linux than on Windows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;better-for-my-company&quot;&gt;Better for my company&lt;&#x2F;h2&gt;
&lt;p&gt;Because I use Linux, my company saves time and money.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Administering machines is easier.&lt;&#x2F;strong&gt; I get Ubuntu and corporate security updates automatically through the built-in security update mechanisms. The corporate updates are maintained by volunteers at my work in their spare time.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;It&#x27;s cheaper.&lt;&#x2F;strong&gt; There is no license fee to use Ubuntu, and no added-value version to buy. Support can be paid for from several companies, including the company behind Ubuntu, Canonical.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;I am more productive.&lt;&#x2F;strong&gt; As explained above, I am able to do more work in the same time because I use Linux.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>LibreOffice spell check not working in Lubuntu</title>
          <pubDate>Wed, 12 Nov 2014 20:28:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/11/12/libreoffice-spell-check-not-working-in-lubuntu/</link>
          <guid>https://artificialworlds.net/blog/2014/11/12/libreoffice-spell-check-not-working-in-lubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/11/12/libreoffice-spell-check-not-working-in-lubuntu/">&lt;p&gt;I installed LibreOffice into Lubuntu, but the spell checking didn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out I need to install a dictionary for my locale:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install myspell-en-gb
&lt;&#x2F;pre&gt;
&lt;p&gt;I needed to restart LibreOffice, and then I got spell checking as I typed.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Is it ok to represent dates as Unix time (seconds since the epoch) in JSON?</title>
          <pubDate>Thu, 06 Nov 2014 14:00:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/11/06/is-it-ok-to-represent-dates-as-unix-time-seconds-since-the-epoch-in-json/</link>
          <guid>https://artificialworlds.net/blog/2014/11/06/is-it-ok-to-represent-dates-as-unix-time-seconds-since-the-epoch-in-json/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/11/06/is-it-ok-to-represent-dates-as-unix-time-seconds-since-the-epoch-in-json/">&lt;p&gt;Yes.&lt;&#x2F;p&gt;
&lt;p&gt;If your JSON contains integer numbers that represent &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unix_time&quot;&gt;Unix time&lt;&#x2F;a&gt; (seconds since the &quot;epoch&quot;), and you parse your JSON to JavaScript, the range of integers that can accurately be represented is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;JavaScript&#x2F;Reference&#x2F;Global_Objects&#x2F;Number?redirectlocale=en-US&amp;amp;redirectslug=JavaScript%2FReference%2FGlobal_Objects%2FNumber#Example:_Using_Number_to_convert_a_Date_object&quot;&gt;-9007199254740992 to 9007199254740992&lt;&#x2F;a&gt; (ref: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.ecma-international.org&#x2F;publications&#x2F;files&#x2F;ECMA-ST&#x2F;Ecma-262.pdf&quot;&gt;EcmaScript standard&lt;&#x2F;a&gt;, section 8.5).&lt;&#x2F;p&gt;
&lt;p&gt;The year 4000AD starts at unix time 64060588815, which is 9007135194152192 less than the maximum.&lt;&#x2F;p&gt;
&lt;p&gt;The year 1AD started at unix time -2177452800, which is 9007197077288192 more than the minimum.&lt;&#x2F;p&gt;
&lt;p&gt;So there are enough numbers.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using hexdump to represent a binary file in Java source code</title>
          <pubDate>Wed, 05 Nov 2014 17:03:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/11/05/using-hexdump-to-represent-a-binary-file-in-java-source-code/</link>
          <guid>https://artificialworlds.net/blog/2014/11/05/using-hexdump-to-represent-a-binary-file-in-java-source-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/11/05/using-hexdump-to-represent-a-binary-file-in-java-source-code/">&lt;p&gt;To embed a binary file (e.g. a png image) into Java source code:&lt;&#x2F;p&gt;
&lt;pre&gt;$ hexdump -v -e &#x27;&#x2F;1 &quot;%3i, &quot;&#x27; myfile.png
-119,  80,  78,  71,  13,  10,  26,  10,   0,   0,   0,  13,  73,  72,  68,  82, ...
&lt;&#x2F;pre&gt;
&lt;p&gt;Copy and paste the output into Java code like this:&lt;&#x2F;p&gt;
&lt;pre&gt;private static final byte[] myfile = { (byte)-119,  80,  78,  71, ... };
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Disabling screensaver on LXDE</title>
          <pubDate>Sat, 18 Oct 2014 09:17:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/10/18/disabling-screensaver-on-lxde/</link>
          <guid>https://artificialworlds.net/blog/2014/10/18/disabling-screensaver-on-lxde/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/10/18/disabling-screensaver-on-lxde/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;caffeine&quot;&gt;Caffeine&lt;&#x2F;a&gt; wasn&#x27;t working for me. I got an error like this:&lt;&#x2F;p&gt;
&lt;pre&gt;dbus.exceptions.DBusException: org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.ScreenSaver was not provided by any .service files
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;caffeine&#x2F;+bug&#x2F;1280449&quot;&gt;Bug 1280449&lt;&#x2F;a&gt; contained the solution for me. Thank you Paulius Vitkus:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-add-repository ppa:behda&#x2F;ppa
sudo apt-get remove caffeine
sudo apt-get update &amp;amp;&amp;amp; sudo apt-get install caffeine
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Android development - saving state</title>
          <pubDate>Fri, 17 Oct 2014 07:16:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/10/17/android-development-saving-state/</link>
          <guid>https://artificialworlds.net/blog/2014/10/17/android-development-saving-state/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/10/17/android-development-saving-state/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;11&#x2F;android-programming-setting-up-android-studio&#x2F;&quot;&gt;Setup&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;17&#x2F;android-programming-a-menu-using-activities-and-intents&#x2F;&quot;&gt;Menu&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;21&#x2F;android-programming-drawing-bitmaps-in-a-game-loop&#x2F;&quot;&gt;Drawing&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;24&#x2F;android-programming-a-non-android-android-game&#x2F;&quot;&gt;non-Android&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;07&#x2F;android-development-rabbit-escape-really-working-on-android&#x2F;&quot;&gt;Working&lt;&#x2F;a&gt;, Saving state.&lt;&#x2F;p&gt;
&lt;p&gt;Android apps like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; need to save their state when asked, and restore themselves later. This happens when you rotate the screen, and could happen at various other times. Here&#x27;s how I handled that in Rabbit Escape:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android development - saving state&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;ce4b5c50-c2f1-4334-84ef-d8c631113e15&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Android development - Rabbit Escape really working on Android</title>
          <pubDate>Tue, 07 Oct 2014 20:04:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/10/07/android-development-rabbit-escape-really-working-on-android/</link>
          <guid>https://artificialworlds.net/blog/2014/10/07/android-development-rabbit-escape-really-working-on-android/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/10/07/android-development-rabbit-escape-really-working-on-android/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;11&#x2F;android-programming-setting-up-android-studio&#x2F;&quot;&gt;Setup&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;17&#x2F;android-programming-a-menu-using-activities-and-intents&#x2F;&quot;&gt;Menu&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;21&#x2F;android-programming-drawing-bitmaps-in-a-game-loop&#x2F;&quot;&gt;Drawing&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;24&#x2F;android-programming-a-non-android-android-game&#x2F;&quot;&gt;non-Android&lt;&#x2F;a&gt;, Working, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;17&#x2F;android-development-saving-state&#x2F;&quot;&gt;Saving state&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Up until now, you weren&#x27;t sure to believe my promises that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;rabbit-escape&quot;&gt;Rabbit Escape&lt;&#x2F;a&gt; really was going to be an Android game, since I hadn&#x27;t actually got it running on Android. Well, the wait is over:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android development - Rabbit Escape really working on Android&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;104ea57b-8107-425c-8b62-53a514499eea&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Showing urgent (flashing) windows on all desktop in LXDE&#x27;s taskbar (window list) in Lubuntu</title>
          <pubDate>Thu, 25 Sep 2014 23:55:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/25/showing-urgent-windows-on-all-desktop-in-lxdes-taskbar-window-list-in-lubuntu/</link>
          <guid>https://artificialworlds.net/blog/2014/09/25/showing-urgent-windows-on-all-desktop-in-lxdes-taskbar-window-list-in-lubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/25/showing-urgent-windows-on-all-desktop-in-lxdes-taskbar-window-list-in-lubuntu/">&lt;p&gt;As it stands, lxpanel&#x27;s &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lxde.svn.sourceforge.net&#x2F;viewvc&#x2F;lxde&#x2F;trunk&#x2F;lxpanel&#x2F;src&#x2F;plugins&#x2F;taskbar.c?revision=2334&amp;amp;view=markup&quot;&gt;taskbar plugin&lt;&#x2F;a&gt; (part of the LXDE desktop you get with Lubuntu) does not show urgent (flashing) windows if they are on a different virtual desktop from the one you are looking at.&lt;&#x2F;p&gt;
&lt;p&gt;This seems wrong to me (I logged &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;lxde&#x2F;bugs&#x2F;682&#x2F;&quot;&gt;Bug 682&lt;&#x2F;a&gt;), and the fix is a little one-liner:&lt;&#x2F;p&gt;
&lt;pre&gt;--- lxpanel.orig&#x2F;src&#x2F;plugins&#x2F;taskbar.c  2013-08-27 23:57:55.000000000 +0100
+++ lxpanel&#x2F;src&#x2F;plugins&#x2F;taskbar.c       2014-09-26 00:48:25.026855589 +0100
@@ -202,10 +202,10 @@
     tk-&amp;gt;flash_timeout = g_timeout_add(interval, (GSourceFunc) flash_window_timeout, tk);
 }

-&#x2F;* Determine if a task is visible considering only its desktop placement. *&#x2F;
+&#x2F;* Determine if a task is visible considering only its desktop placement and urgency. *&#x2F;
 static gboolean task_is_visible_on_current_desktop(TaskbarPlugin * tb, Task * tk)
 {
-    return ((tk-&amp;gt;desktop == ALL_WORKSPACES) || (tk-&amp;gt;desktop == tb-&amp;gt;current_desktop) || (tb-&amp;gt;show_all_desks));
+    return ((tk-&amp;gt;desktop == ALL_WORKSPACES) || (tk-&amp;gt;desktop == tb-&amp;gt;current_desktop) || (tb-&amp;gt;show_all_desks) || tk-&amp;gt;urgency);
 }

 &#x2F;* Recompute the visible task for a class when the class membership changes.
&lt;&#x2F;pre&gt;
&lt;p&gt;To install this patch into Lubuntu, do something like this:&lt;pre&gt;sudo apt-get install build-essential fakeroot dpkg-dev
sudo apt-get build-dep lxpanel
mkdir lxpanel
cd lxpanel
apt-get source lxpanel
cd lxpanel-*
wget https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;lxde&#x2F;bugs&#x2F;682&#x2F;attachment&#x2F;show-urgent-windows-on-all-desktops.patch
patch -p1 &amp;lt; show-urgent-windows-on-all-desktops.patch
dpkg-buildpackage -rfakeroot -b
cd ..
sudo dpkg -i lxpanel_*.deb
killall lxpanel
lxpanel --profile Lubuntu
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Launch an urgent window using Python and Xlib with the UrgencyHint flag</title>
          <pubDate>Thu, 25 Sep 2014 23:25:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/25/launch-an-urgent-window-using-python-and-xlib-with-the-urgencyhint-flag/</link>
          <guid>https://artificialworlds.net/blog/2014/09/25/launch-an-urgent-window-using-python-and-xlib-with-the-urgencyhint-flag/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/25/launch-an-urgent-window-using-python-and-xlib-with-the-urgencyhint-flag/">&lt;p&gt;I am trying to fix a bug in lxpanel&#x27;s taskbar plugin, and needed to launch an urgent window. Here&#x27;s how I did it in a little python.&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;python

# urgent.py -- launch an urgent window (Copyright messages are at the bottom)

# To use:
# sudo apt-get install python-xlib
# .&#x2F;urgent.py

import sys
import Xlib
from Xlib import X, display, Xutil

d = display.Display()
s = d.screen()
w = s.root.create_window(
    50, 50, 300, 200, 2,
    s.root_depth,
    X.InputOutput,
    X.CopyFromParent,
    background_pixel = s.white_pixel,
)

w.set_wm_name( &#x27;Urgent!&#x27; )
w.set_wm_hints( flags = Xutil.UrgencyHint )

w.map()

try:
    while 1:
        e = d.next_event()
except Xlib.error.ConnectionClosedError:
    pass

# This code is based on:
# examples&#x2F;xrandr.py -- demonstrate the RandR extension
# from http:&#x2F;&#x2F;python-xlib.sourceforge.net&#x2F;
#
#    Copyright (C) 2014 Andy Balaam
#    Copyright (C) 2009 David H. Bronke
#
#    This program is free software; you can redistribute it and&#x2F;or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Android programming - a non-Android, Android Game</title>
          <pubDate>Wed, 24 Sep 2014 06:04:03 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/24/android-programming-a-non-android-android-game/</link>
          <guid>https://artificialworlds.net/blog/2014/09/24/android-programming-a-non-android-android-game/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/24/android-programming-a-non-android-android-game/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;11&#x2F;android-programming-setting-up-android-studio&#x2F;&quot;&gt;Setup&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;17&#x2F;android-programming-a-menu-using-activities-and-intents&#x2F;&quot;&gt;Menu&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;21&#x2F;android-programming-drawing-bitmaps-in-a-game-loop&#x2F;&quot;&gt;Drawing&lt;&#x2F;a&gt;, non-Android, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;07&#x2F;android-development-rabbit-escape-really-working-on-android&#x2F;&quot;&gt;Working&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;17&#x2F;android-development-saving-state&#x2F;&quot;&gt;Saving state&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re planning to write an Android game. So why would we deliberately avoid Android while we&#x27;re writing it? To make sure we&#x27;re not overly-dependent on the Android ways of doing things, and are able to run our tests etc. on the local machine:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android development - a non-Android, Android game?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;5e08f71d-97b0-4811-bea2-10f92397dd85&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Android programming - drawing bitmaps in a game loop</title>
          <pubDate>Sun, 21 Sep 2014 19:12:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/21/android-programming-drawing-bitmaps-in-a-game-loop/</link>
          <guid>https://artificialworlds.net/blog/2014/09/21/android-programming-drawing-bitmaps-in-a-game-loop/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/21/android-programming-drawing-bitmaps-in-a-game-loop/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;11&#x2F;android-programming-setting-up-android-studio&#x2F;&quot;&gt;Setup&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;17&#x2F;android-programming-a-menu-using-activities-and-intents&#x2F;&quot;&gt;Menu&lt;&#x2F;a&gt;, Drawing, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;24&#x2F;android-programming-a-non-android-android-game&#x2F;&quot;&gt;non-Android&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;07&#x2F;android-development-rabbit-escape-really-working-on-android&#x2F;&quot;&gt;Working&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;17&#x2F;android-development-saving-state&#x2F;&quot;&gt;Saving state&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I will be talking about how to write Android programs that share code with non-Android programs. The program I will be writing is a simple game.&lt;&#x2F;p&gt;
&lt;p&gt;This time we&#x27;re writing a game loop and drawing images onto a Canvas using a SurfaceView:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android programming - drawing bitmaps in a game loop&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a3259508-2d32-45ea-b0d8-714baf74f901&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Don&#x27;t use email auto-forwarding (emails will go missing)</title>
          <pubDate>Fri, 19 Sep 2014 02:47:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/19/dont-use-email-auto-forwarding-emails-will-go-missing/</link>
          <guid>https://artificialworlds.net/blog/2014/09/19/dont-use-email-auto-forwarding-emails-will-go-missing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/19/dont-use-email-auto-forwarding-emails-will-go-missing/">&lt;p&gt;I&#x27;ve been trying to chase down a mysterious problem with emails going missing. The problem with things being missing is that you can&#x27;t see them, so it has been difficult to track down exactly what&#x27;s going on, but it became clear that we were receiving no email at all from Amazon or Play.com for several months, and that some other mails had disappeared too. I was secretly assuming that I&#x27;d screwed something up in the terrifying postfix+dovecot+fetchmail+spamassassin+procmail config on our server, but I couldn&#x27;t find any indication of what could be going wrong.&lt;&#x2F;p&gt;
&lt;p&gt;I finally got around to considering whether it was someone else&#x27;s fault, and very surprisingly, my ISP &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.plus.net&#x2F;&quot;&gt;Plusnet&lt;&#x2F;a&gt; and my web hosts &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bluehost.com&#x2F;&quot;&gt;Bluehost&lt;&#x2F;a&gt; were both very responsive and helpful when I contacted them.&lt;&#x2F;p&gt;
&lt;p&gt;I thought an over-enthusiastic spam filter might be swallowing mails, but it turns out the problem was that I was &quot;forwarding&quot; emails from my domain to my ISP-provided mailbox.&lt;&#x2F;p&gt;
&lt;p&gt;Since the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Sender_Policy_Framework&quot;&gt;SPF&lt;&#x2F;a&gt; anti-spam system has begun to be implemented, emails from people like Amazon come with SPF policies that make &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Email_forwarding#Server-based_forwarding&quot;&gt;plain message forwarding&lt;&#x2F;a&gt; fail, because forwarding makes it look like emails come from the server forwarding them, when actually they come from the original sender. This makes it impossible to do SPF, so the emails were getting silently deleted.&lt;&#x2F;p&gt;
&lt;p&gt;Now that I know what the problem is, I have even found a sentence describing this on the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Sender_Policy_Framework&quot;&gt;Wikipedia page on SPF&lt;&#x2F;a&gt;: &quot;SPF breaks plain message forwarding&quot; and this useful article: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.richweb.com&#x2F;why_email_forwarding_is_broken&quot;&gt;Why forwarding your email is a BAD idea&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The only solution I can see is to set up a mailbox on my domain host&#x27;s site, and stop using the mailbox provided by my ISP. I was only using this mailbox as a holding area before I pulled down the mail via POP3, so it doesn&#x27;t actually make much difference to me.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re using GMail you can set it up to pull from a POP3 server, and some other webmail services let you do that too, so using your domain host&#x27;s web server doesn&#x27;t have to be inconvenient.&lt;&#x2F;p&gt;
&lt;p&gt;Losing email makes me stressed.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Marie Seltenrych
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I read this with interest as I have a similar problem.  I thought I fixed forwarding emails but thus far no emails are getting through.  I do also have a Gmail account (plus 2 others). Your comments give me hope once again that I am not a complete idiot. Thanks a lot. Marie :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Yes, I live in perpetual confusion about how it really works, and perturb it until it seems to work when it stops.  This is obviously not a good security posture :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Fetchmail &quot;mda&quot; option makes emails bypass the spamassassin filter in postfix</title>
          <pubDate>Fri, 19 Sep 2014 02:19:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/19/fetchmail-mda-option-makes-emails-bypass-the-spamassassin-filter-in-postfix/</link>
          <guid>https://artificialworlds.net/blog/2014/09/19/fetchmail-mda-option-makes-emails-bypass-the-spamassassin-filter-in-postfix/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/19/fetchmail-mda-option-makes-emails-bypass-the-spamassassin-filter-in-postfix/">&lt;p&gt;I have spent several months with no spam filtering in my postfix+dovecote+spamassassin+fetchmail+procmail setup, and no idea why. I had spamassassin enabled and working when I piped emails into it manually, and I had lines like this in my postfix master.cf:&lt;&#x2F;p&gt;
&lt;pre&gt;# Listen on port 2025, not 25, and filter with the spamassassin service
2025      inet  n       -       -       -       -       smtpd
	-o content_filter=spamassassin
# ...

# Definition of the spamassassin service: pipe to spamc, then sendmail
spamassassin unix -     n       n       -       -       pipe
	user=spamd argv=&#x2F;usr&#x2F;bin&#x2F;spamc -x -e    
	&#x2F;usr&#x2F;sbin&#x2F;sendmail -oi -f ${sender} ${recipient}
&lt;&#x2F;pre&gt;
&lt;p&gt;But no matter what I did, I saw no spamassassin headers in my emails, and no mention of using it in the logs. Frustratingly, it was filtering perfectly on outgoing mail, which I really don&#x27;t need...&lt;&#x2F;p&gt;
&lt;p&gt;Today I worked out what the problem was - nothing to do with postfix or spamassassin config, but to do with my .fetchmailrc.&lt;&#x2F;p&gt;
&lt;p&gt;Because I was running on a non-standard port, and also because I had tried not allowing unencrypted connections to postfix even from localhost, I was using telling fetchmail to deliver with &#x2F;usr&#x2F;sbin&#x2F;sendmail, instead of allowing it to connect to postfix on port 25 as it normally does. So my .fetchmailrc contained:&lt;&#x2F;p&gt;
&lt;pre&gt;poll example.com with proto POP3
    user &#x27;x&#x27; there with password &#x27;y&#x27; is &#x27;z&#x27; here
    mda &quot;&#x2F;usr&#x2F;sbin&#x2F;sendmail  -i  -f %F -- %T&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;It turns out this completely bypasses all the filters, and delivers the mail immediately. So, what I needed was the syntax to connect on a non-standard port, which looks like:&lt;&#x2F;p&gt;
&lt;pre&gt;poll example.com with proto POP3
    user &#x27;x&#x27; there with password &#x27;y&#x27; is &#x27;z&#x27; here
    smtphost localhost&#x2F;2025
&lt;&#x2F;pre&gt;
&lt;p&gt;(If your postfix listens on port 25, you don&#x27;t need the smtphost line at all.)&lt;&#x2F;p&gt;
&lt;p&gt;And now my email is being filtered for spam, and put into a Junk folder by procmail.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Android programming - a menu using Activities and Intents</title>
          <pubDate>Wed, 17 Sep 2014 07:04:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/17/android-programming-a-menu-using-activities-and-intents/</link>
          <guid>https://artificialworlds.net/blog/2014/09/17/android-programming-a-menu-using-activities-and-intents/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/17/android-programming-a-menu-using-activities-and-intents/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;11&#x2F;android-programming-setting-up-android-studio&#x2F;&quot;&gt;Setup&lt;&#x2F;a&gt;, Menu, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;21&#x2F;android-programming-drawing-bitmaps-in-a-game-loop&#x2F;&quot;&gt;Drawing&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;24&#x2F;android-programming-a-non-android-android-game&#x2F;&quot;&gt;non-Android&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;07&#x2F;android-development-rabbit-escape-really-working-on-android&#x2F;&quot;&gt;Working&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;17&#x2F;android-development-saving-state&#x2F;&quot;&gt;Saving state&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I will be talking about how to write Android programs that share code with non-Android programs. The program I will be writing is a simple game.&lt;&#x2F;p&gt;
&lt;p&gt;This time we&#x27;re making a simple menu structure:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android programming - a menu activity&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a3a95493-a76e-47c2-9c50-29e33e7c2591&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>Raspberry Pi Minecraft Python summer project screenshots</title>
          <pubDate>Tue, 16 Sep 2014 20:39:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/16/raspberry-pi-minecraft-python-summer-project-screenshots/</link>
          <guid>https://artificialworlds.net/blog/2014/09/16/raspberry-pi-minecraft-python-summer-project-screenshots/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/16/raspberry-pi-minecraft-python-summer-project-screenshots/">&lt;blockquote&gt;
&lt;p&gt;[Screenshots of Minecraft on the Raspberry Pi can&#x27;t be made with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gettingstartedwithraspberrypi.tumblr.com&#x2F;post&#x2F;24142374137&#x2F;setting-up-a-vnc-server&quot;&gt;VNC&lt;&#x2F;a&gt; or screenshot tool like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.maketecheasier.com&#x2F;take-screenshot-raspberry-pi&#x2F;&quot;&gt;scrot&lt;&#x2F;a&gt;, but they can be made with the excellent &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.raspberrypi-spy.co.uk&#x2F;2014&#x2F;05&#x2F;how-to-capture-minecraft-screenshots-on-the-raspberry-pi&#x2F;&quot;&gt;raspi2png&lt;&#x2F;a&gt;.]&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;We did some Python programming in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;pi.minecraft.net&#x2F;&quot;&gt;Minecraft on the Raspberry Pi&lt;&#x2F;a&gt; for our summer projects.&lt;&#x2F;p&gt;
&lt;p&gt;Child 2 made some lovely houses and enjoyed destroying things much more efficiently than when you do it by hand:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;houses.png&quot; alt=&quot;houses&quot; width=&quot;800&quot; height=&quot;412&quot; class=&quot;aligncenter size-full wp-image-1491&quot;&gt;
&lt;p&gt;and Child 0 made a spell book. You can see the &quot;elements&quot; spell has been cast in the background (earth, air, water and fire), the &quot;topsy-turvy&quot; spell on the right, the &quot;frozen&quot; spell on the left, and on the far left you can just see a bit of the &quot;river&quot; spell:&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;spells.png&quot; alt=&quot;spells&quot; width=&quot;800&quot; height=&quot;412&quot; class=&quot;aligncenter size-full wp-image-1492&quot;&gt;
&lt;p&gt;To cast spells you must first utter the magical incantations:&lt;&#x2F;p&gt;
&lt;pre&gt;python
&lt;&#x2F;pre&gt;
&lt;p&gt;and then:&lt;&#x2F;p&gt;
&lt;pre&gt;from spells import *
&lt;&#x2F;pre&gt;
&lt;p&gt;then each spell can be cast by simply saying its name followed by &lt;em&gt;the double brackets of power&lt;&#x2F;em&gt;, for example:&lt;&#x2F;p&gt;
&lt;pre&gt;topsyturvy()
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Android programming - setting up Android Studio</title>
          <pubDate>Thu, 11 Sep 2014 06:57:59 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/09/11/android-programming-setting-up-android-studio/</link>
          <guid>https://artificialworlds.net/blog/2014/09/11/android-programming-setting-up-android-studio/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/09/11/android-programming-setting-up-android-studio/">&lt;p&gt;Series: Setup, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;17&#x2F;android-programming-setting-up-android-studio-2&#x2F;&quot;&gt;Menu&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;21&#x2F;android-programming-drawing-bitmaps-in-a-game-loop&#x2F;&quot;&gt;Drawing&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;09&#x2F;24&#x2F;android-programming-a-non-android-android-game&#x2F;&quot;&gt;non-Android&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;07&#x2F;android-development-rabbit-escape-really-working-on-android&#x2F;&quot;&gt;Working&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;10&#x2F;17&#x2F;android-development-saving-state&#x2F;&quot;&gt;Saving state&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I will be talking about how to write Android programs that share code with non-Android programs. The program I will be writing is a simple game.&lt;&#x2F;p&gt;
&lt;p&gt;First, how to set up Android Studio:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Android programming - setting up Android Studio&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;613d52a2-dd2f-4c79-8b4d-f93d0695c900&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>What is a string?</title>
          <pubDate>Sat, 23 Aug 2014 08:34:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/08/23/what-is-a-string/</link>
          <guid>https://artificialworlds.net/blog/2014/08/23/what-is-a-string/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/08/23/what-is-a-string/">&lt;p&gt;Most programming languages have some wrinkles around unicode and strings*. In my ficticious language &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;artificialworlds.net&#x2F;pepper&#x2F;&quot;&gt;Pepper&lt;&#x2F;a&gt;, there are no wrinkles of any kind, and everything is perfect.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;*E.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mathiasbynens.be&#x2F;notes&#x2F;javascript-unicode&quot;&gt;JavaScript&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;1527856&#x2F;how-can-i-iterate-through-the-unicode-codepoints-of-a-java-string&quot;&gt;Java&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.kfish.org&#x2F;2007&#x2F;10&#x2F;survey-haskell-unicode-support.html&quot;&gt;Haskell&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;graysoftinc.com&#x2F;character-encodings&#x2F;understanding-m17n-multilingualization&quot;&gt;Ruby&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3.3&#x2F;howto&#x2F;unicode.html&quot;&gt;Python&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;There are several key concepts. The most important are an interface AnyString and the variable** String which is what you should use when you are writing code with strings.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;**String is a variable that refers to a type, so you just use it like a type and don&#x27;t worry about it.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre&gt;interface AnyString
{
    def indexable(CodePoint) code_points( implements(AnyString) string )
}
&lt;&#x2F;pre&gt;
&lt;p&gt;In Pepper an interface can describe what free functions exist as well as what member function a class must have, and here we just require that a code_points function exists that gives us a collection of CodePoint objects that may be indexed (i.e. is random-access).&lt;&#x2F;p&gt;
&lt;p&gt;When your Pepper program starts, the String variable will refer to something that implements this interface, and probably some other interfaces too. Most Pepper programs will use a String that is implemented as an array of bytes representing a string in UTF-8, but the programmer doesn&#x27;t need to be aware of that, and in a situation where something different is needed (e.g. where we know lots of non-Latin characters will be used and UTF-16 will be more efficient) String can be set to something different in the configuration settings used by the compiler.&lt;&#x2F;p&gt;
&lt;p&gt;When you want to do something with a string, there will be functions that only rely on the AnyString interface and deal with CodePoints internally, but there will be other overloads that are potentially more efficient, for example there are two versions of the standard print function:&lt;&#x2F;p&gt;
&lt;pre&gt;def void print( implements(AnyString) string )
def void print( NativeUtf8String string )
&lt;&#x2F;pre&gt;
&lt;p&gt;The NativeUtf8String class is implemented as a std::string in the C++ code emitted by the Pepper compiler, and the most efficient way to represent an array of bytes when compiling onto other platforms, so the version of print that uses it can be quite efficient.&lt;&#x2F;p&gt;
&lt;p&gt;Because all these types are known at compile time, the C++ code generated by the Pepper compiler can use the native types directly (and be efficient), even though the programmer is writing code using just the AnyString and String types, meaning their code can be adapted to other platforms by using a different configuration.&lt;&#x2F;p&gt;
&lt;p&gt;The Pepper environment exposes standard-out and standard-in as UTF-8 streams, and takes care of converting to the platform encoding for you (at runtime).&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Absolute Truth in programming languages</title>
          <pubDate>Fri, 22 Aug 2014 09:38:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/08/22/absolute-truth-in-programming-languages/</link>
          <guid>https://artificialworlds.net/blog/2014/08/22/absolute-truth-in-programming-languages/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/08/22/absolute-truth-in-programming-languages/">&lt;p&gt;Is enforcing truthfulness the opposite of beauty?&lt;&#x2F;p&gt;
&lt;p&gt;Can 2 + 2 = 5?&lt;&#x2F;p&gt;
&lt;p&gt;Improvements, corrections, further contributions are welcome.&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat five.cpp
#include &amp;lt;iostream&amp;gt;
int operator+( int x, int y ) { return 5; }
int main() {
    std::cout &amp;lt;&amp;lt; 2 + 2 &amp;lt;&amp;lt; std::endl;
}
$ g++ five.cpp
five.cpp:2:29: error: â€˜int operator+(int, int)â€™ must have an argument of class or enumerated type
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ python
&amp;gt;&amp;gt;&amp;gt; int.__add__ = lambda y: 5
TypeError: can&#x27;t set attributes of built-in&#x2F;extension type &#x27;int&#x27;
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ cat five.hs
import Prelude hiding ((+))
x + y = 5
main = print ( 2 + 2 )
$ ghc five.hs &amp;amp;&amp;amp; .&#x2F;five
5
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ cat five.rb
class Fixnum
    def +(y)
        5
    end
end
print 2 + 2
$ ruby five.rb
5
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ mzscheme
&amp;gt; (define (+ x y) 5)
&amp;gt; (+ 2 2)
5
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Options for code reviews with Git</title>
          <pubDate>Mon, 18 Aug 2014 10:19:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/08/18/options-for-code-reviews-with-git/</link>
          <guid>https://artificialworlds.net/blog/2014/08/18/options-for-code-reviews-with-git/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/08/18/options-for-code-reviews-with-git/">&lt;p&gt;We&#x27;re thinking about switching to Git for my work, and I want to be confident we can still support good code reviews if we make the switch.&lt;&#x2F;p&gt;
&lt;p&gt;I am a big fan of in-person reviews, and for that, git difftool is enough but sometimes you need to do it asynchronously, and then you need a tool or a process or something.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the options as I see them so far (please comment if you know others I should consider):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Emailing patches.&lt;&#x2F;strong&gt; Git has git format-patch and git bundle that allow creating a file containing changes that can be sent by email or message. These can be reviewed as patches or applied to the working tree and reviewed in context.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Feature branch and pull request.&lt;&#x2F;strong&gt; Devs to push their changes to a branch in a shared repo and send an email or message asking a colleague to pull the branch. The reviewer looks at the changes in the repo or pulls them, then either sends back comments, or merges the branch into their own and delivers to the master branch.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tools.&lt;&#x2F;strong&gt; There are several extra tools that sit in front of Git and deliver changes when they are reviewed. These include: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;code.google.com&#x2F;p&#x2F;gerrit&#x2F;&quot;&gt;Gerrit&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jensl&#x2F;critic&quot;&gt;Critic&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.reviewboard.org&#x2F;&quot;&gt;Review Board&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;References:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;5319390&#x2F;using-git-for-code-reviews#5319836&quot;&gt;StackOverflow: Using git for Code Reviews?&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;nvie.com&#x2F;posts&#x2F;a-successful-git-branching-model&#x2F;&quot;&gt;A successful Git branching model&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.carbonfive.com&#x2F;2010&#x2F;11&#x2F;01&#x2F;integrating-topic-branches-in-git&#x2F;&quot;&gt;Integrating topic branches in Git&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Desktop sharing not working in Lubuntu 14.04</title>
          <pubDate>Tue, 12 Aug 2014 14:57:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/08/12/desktop-sharing-not-working-in-lubuntu-14-04/</link>
          <guid>https://artificialworlds.net/blog/2014/08/12/desktop-sharing-not-working-in-lubuntu-14-04/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/08/12/desktop-sharing-not-working-in-lubuntu-14-04/">&lt;p&gt;On my recently-upgraded to 14.04 Lubuntu machine, desktop sharing didn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I made it work:&lt;&#x2F;p&gt;
&lt;p&gt;Once (to allow some Windows clients to connect without encryption):&lt;&#x2F;p&gt;
&lt;pre&gt;gsettings set org.gnome.Vino require-encryption false&lt;&#x2F;pre&gt;
&lt;p&gt;Then every time I want to share my desktop:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;usr&#x2F;lib&#x2F;vino&#x2F;vino-server&lt;&#x2F;pre&gt;
&lt;p&gt;and leave that running while I&#x27;m sharing.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Best GCC warning flags for compiling C++</title>
          <pubDate>Fri, 18 Jul 2014 09:01:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/18/best-gcc-warning-flags-for-compiling-c/</link>
          <guid>https://artificialworlds.net/blog/2014/07/18/best-gcc-warning-flags-for-compiling-c/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/18/best-gcc-warning-flags-for-compiling-c/">&lt;p&gt;A recent discussion on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;mailman&#x2F;listinfo&#x2F;accu-general&quot;&gt;ACCU-general&lt;&#x2F;a&gt; gave people an opportunity to share the warning flags they like to use with g++.&lt;&#x2F;p&gt;
&lt;p&gt;I thought I&#x27;d write down the consensus as I understood it, mainly for my own reference:&lt;&#x2F;p&gt;
&lt;pre&gt;-Wredundant-decls
-Wcast-align
-Wmissing-declarations
-Wmissing-include-dirs
-Wswitch-enum
-Wswitch-default
-Wextra
-Wall
-Werror
-Winvalid-pch
-Wredundant-decls
-Wmissing-prototypes
-Wformat=2
-Wmissing-format-attribute
-Wformat-nonliteral
&lt;&#x2F;pre&gt;
&lt;p&gt;We were advised by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;advogato.org&#x2F;person&#x2F;redi&#x2F;&quot;&gt;Jonathan Wakely&lt;&#x2F;a&gt; that -Weffc++ is not very useful since it is mostly based on the first edition of the book Effective C++, many of whose recommendations were improved in the second edition, and also apparently GCC doesn&#x27;t do a great job of warning about them.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; thanks to an article[1] by Roger Orr in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journal&quot;&gt;CVu&lt;&#x2F;a&gt; these flags are highly recommended in GCC 5.2+:&lt;&#x2F;p&gt;
&lt;pre&gt;-flto
-Wodr
&lt;&#x2F;pre&gt;
&lt;p&gt;[1] Orr, Roger &quot;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;2167&quot;&gt;One Definition Rule&lt;&#x2F;a&gt;&quot;, in CVu Volume 27, Issue 5 p16 (editor: Steve Love)&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    No Name
    &lt;span class=&quot;comment_date&quot;&gt;2019-05-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;-Wredundant-decls is duplicated&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Martin Moene
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Andy,&lt;&#x2F;p&gt;
&lt;p&gt;Would be nice to have each one linked to the right spot in the documentation [1], but that seems unsupported.&lt;&#x2F;p&gt;
&lt;p&gt;What advised the ordering of this list?&lt;&#x2F;p&gt;
&lt;p&gt;cheers,&lt;br&#x2F;&gt;
Martin&lt;&#x2F;p&gt;
&lt;p&gt;[1] e.g. &lt;a href=&quot;http:&#x2F;&#x2F;gcc.gnu.org&#x2F;onlinedocs&#x2F;gcc&#x2F;Warning-Options.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;gcc.gnu.org&#x2F;onlinedocs&#x2F;gcc&#x2F;Warning-Opt...&lt;&#x2F;a&gt;, -Wcast-align&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Martin, I don’t think the order matters does it?  I just copied in from people’s emails in the order I found them…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Martin Moene
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy,&lt;&#x2F;p&gt;
&lt;p&gt;The compiler probably cares less :)&lt;&#x2F;p&gt;
&lt;p&gt;-Wall&lt;br&#x2F;&gt;
-Wcast-align&lt;br&#x2F;&gt;
-Werror&lt;br&#x2F;&gt;
-Wextra&lt;br&#x2F;&gt;
-Wformat-nonliteral&lt;br&#x2F;&gt;
-Wformat=2&lt;br&#x2F;&gt;
-Winvalid-pch&lt;br&#x2F;&gt;
-Wmissing-declarations&lt;br&#x2F;&gt;
-Wmissing-format-attribute&lt;br&#x2F;&gt;
-Wmissing-include-dirs&lt;br&#x2F;&gt;
-Wmissing-prototypes&lt;br&#x2F;&gt;
-Wredundant-decls&lt;br&#x2F;&gt;
-Wredundant-decls&lt;br&#x2F;&gt;
-Wswitch-default&lt;br&#x2F;&gt;
-Wswitch-enum&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>What git server should I use?</title>
          <pubDate>Tue, 15 Jul 2014 15:40:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/15/what-git-server-should-i-use/</link>
          <guid>https://artificialworlds.net/blog/2014/07/15/what-git-server-should-i-use/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/15/what-git-server-should-i-use/">&lt;p&gt;At work we are considering whether we can use Git for our source control. I am a big fan of Git, so I&#x27;d like to see this happen.&lt;&#x2F;p&gt;
&lt;p&gt;We only need to work against a central repository most of the time, so I&#x27;m looking at what servers might work for us.&lt;&#x2F;p&gt;
&lt;p&gt;Update: This StackExchange question may help: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;softwarerecs.stackexchange.com&#x2F;questions&#x2F;867&#x2F;self-hosted-replacement-for-github&quot;&gt;Self-hosted replacement for Github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Update: Added software from the StackExchange answers to the list.&lt;&#x2F;p&gt;
&lt;p&gt;Features we will need:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;User management&lt;&#x2F;li&gt;
&lt;li&gt;Repository management&lt;&#x2F;li&gt;
&lt;li&gt;Browsing code and diffs via the web&lt;&#x2F;li&gt;
&lt;li&gt;Hosted in-house&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Features we might want:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;External user authentication e.g. via LDAP&lt;&#x2F;li&gt;
&lt;li&gt;Code review&lt;&#x2F;li&gt;
&lt;li&gt;Integrating with an issue tracker&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Most of my use of Git so far has been against large servers like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;&quot;&gt;GitHub&lt;&#x2F;a&gt; (which I really like) and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;sourceforge.net&#x2F;&quot;&gt;SourceForge&lt;&#x2F;a&gt;, but recently I set up a test Git server using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gitolite.com&#x2F;gitolite&#x2F;index.html&quot;&gt;gitolite&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;ch4-6.html&quot;&gt;gitweb&lt;&#x2F;a&gt;, which gives me my 4 &quot;needs&quot; above but not my 3 &quot;wants&quot;. It also requires command-line use of git to administer SSH keys for users, so might not suit our system administrators or all our developers.&lt;&#x2F;p&gt;
&lt;p&gt;So, lazyweb, what server should I recommend?&lt;&#x2F;p&gt;
&lt;p&gt;Here is my research so far:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;free-git-server-software&quot;&gt;Free git server software&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;about.gitlab.com&#x2F;&quot;&gt;GitLab&lt;&#x2F;a&gt; - looks a lot like GitHub, and appears to satisfy all 4 of my needs and all 3 of my wants. Might be a bit decentralised (ironically) for our usage e.g. the docs talk about using merge requests for code review whereas I&#x27;d expect we&#x27;d want a commit-gating style which is what I believe Gerrit provides.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.scm-manager.org&#x2F;&quot;&gt;SCM-Manager&lt;&#x2F;a&gt; - looks very corporate. Likely it could satisfy my needs and my wants.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;code.google.com&#x2F;p&#x2F;gerrit&#x2F;&quot;&gt;Gerrit&lt;&#x2F;a&gt; + &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gitblit.com&#x2F;&quot;&gt;Gitblit&lt;&#x2F;a&gt; - lots of code review features, used by major projects including the Android open source project. Weird that Gerrit doesn&#x27;t include a code browser and you have to add something like Gitblit. I think this will give me all 4 of my needs and all 3 of my wants.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gitolite.com&#x2F;gitolite&#x2F;index.html&quot;&gt;Gitolite&lt;&#x2F;a&gt; + &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;git-scm.com&#x2F;book&#x2F;ch4-6.html&quot;&gt;gitweb&lt;&#x2F;a&gt; - this is what I am using at the moment, and it works well, satisfying the needs above, but not the wants. Gitolite configuration is done by editing config files and pushing them into a special git repository on the server. Adding users means adding a user&#x27;s SSH key to the config repository, so requires tech-savvy users and admins. gitweb is fast and clear. My only complaint is that you don&#x27;t seem to be able to control the amount of context you see in a diff (often I want to see the full files).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.getgitorious.com&#x2F;&quot;&gt;Gitorious&lt;&#x2F;a&gt; - the software behind a mature public site that some people really like. Doesn&#x27;t appear to do LDAP authentication, and may not integrate with issue tracking.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tuleap.net&quot;&gt;tuleap&lt;&#x2F;a&gt; - project planning, chat, issue tracking, builds, document management, discussion board, news all in one product. Includes Gerrit for code reviews, Jenkins for build management. Supports LDAP and OpenID authentication.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;phabricator.org&#x2F;&quot;&gt;Phabricator&lt;&#x2F;a&gt; - code hosting and review, issue tracking, wiki, alerts, message boards, blogs, Q&amp;amp;A, polls all in one product. Supports LDAP and OAuth authentication.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gitprep.org&#x2F;&quot;&gt;GitPrep&lt;&#x2F;a&gt; - explicitly a clone of GitHub. Seems to look nice, but a young project and not talked about much on the Internet yet.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;takezoe&#x2F;gitbucket&quot;&gt;GitBucket&lt;&#x2F;a&gt; - explicitly a clone of GitHub. Couldn&#x27;t find much information beyond that.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gitlist.org&#x2F;&quot;&gt;GitList&lt;&#x2F;a&gt; - only a repository viewer, but could possibly be used with gitolite instead of plain gitweb - may have more features such as full-file diffs, but I&#x27;m not sure. Looks pretty, and doesn&#x27;t have much documentation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;git.zx2c4.com&#x2F;cgit&#x2F;about&#x2F;&quot;&gt;CGit&lt;&#x2F;a&gt; - only a repository viewer, but definitely allows specifying the amount of context in diffs (and if you edit the URL directly you can ask for as much as you want). Very plain interface, and minimal documentation. Claims to be fast.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mensi&#x2F;cydra&#x2F;&quot;&gt;Cydra&lt;&#x2F;a&gt; - may turn out to be good but no web site at the moment, so probably not mature enough to consider.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tv42&#x2F;gitosis&#x2F;commits&#x2F;master&quot;&gt;Gitosis&lt;&#x2F;a&gt; appears to be a dead project.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;paid-in-house-git-server-software&quot;&gt;Paid (in-house) git server software&lt;&#x2F;h2&gt;
&lt;p&gt;Very unlikely that we will pay for anything, but here are the options I have found so far:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.atlassian.com&#x2F;software&#x2F;stash&quot;&gt;Atlassian Stash&lt;&#x2F;a&gt; - one-time payment e.g. $6,000 for 100 users.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;enterprise.github.com&#x2F;&quot;&gt;GitHub Enterprise&lt;&#x2F;a&gt; - apparently they do in-house installations but I couldn&#x27;t find any information. GitHub has an excellent interface and features.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.visualstudio.com&#x2F;products&#x2F;tfs-overview-vs&quot;&gt;Microsoft Team Foundation Server&lt;&#x2F;a&gt; - a larger system that offers Git integration as a feature.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rhodecode.com&#x2F;&quot;&gt;RhodeCode&lt;&#x2F;a&gt; - 50 users for $199&#x2F;month, supports LDAP and Active Directory authentication, and code review. Claims to be highly secure.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jesper hogstrom
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You can hook gitolite to an LDAP&#x2F;AD server. We use it extensively, and use the groups to provide access rights. We use Crucible for reviews and not much integration with an issue tracker, but I think it could be done.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I am investigating options. Gitlab is high on the list, though I believe Visual Studio Online might be a big bang replacement for all of it (Jira, Crucible, TeamCity, git&#x2F;gitolite).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks jesper.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jesper HÃ¶gstrÃ¶m
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Obviously, if you want the glue scripts to hook up gitolite to AD, just mail me!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks – GitLab does seem to be emerging as a winner for me too, especially as it now seems to provide a continuous integration server too.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Centralised version control repositories | dazdaztech blog
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] This is also worth a quick read &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2014&#x2F;07&#x2F;15&#x2F;what-git-server-should-i-use&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2014&#x2F;07&#x2F;...&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    DOSUNMU ADETOKUNBO NASIR
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;gitblit any day……now has AD&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    SUNSAN
    &lt;span class=&quot;comment_date&quot;&gt;2014-12-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Which option did you go with?  Would you recommend it to a corp with a staff of approx 50 devs?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Sadly, we haven’t got there yet, but I was thinking GitLab or Gerrit would probably be the right choice.  TBH if I had my own way (which I won’t) I’d go for GitHub itself – not the hosted version, but the real one, with private repos.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    thoroc
    &lt;span class=&quot;comment_date&quot;&gt;2015-01-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I am looking into this for work as well, and I have found at least three more candidates:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;www.deveo.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.deveo.com&#x2F;&lt;&#x2F;a&gt; (commercial) – not tested, but their page looks good&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;gogs.io&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;gogs.io&#x2F;&lt;&#x2F;a&gt; (FOSS) – tested, can be buggy at time, but cross platform and have all the basic you could expect from a GitHub replacement&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;http:&#x2F;&#x2F;bonobogitserver.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;bonobogitserver.com&#x2F;&lt;&#x2F;a&gt; (FOSS) – that one is a bit of an ugly duckling as it is a .Net based one so windows&#x2F;IIS server only&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I have tried Stash and Rhodecode, and although I have heard people raving how good the former is, I have yet to have a good experience with it. Same with Rhodecode. But maybe it was due to the fact that I have to run both on a weak VM.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-01-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the extra info, useful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Renewing self-signed certificate for ejabberd</title>
          <pubDate>Tue, 15 Jul 2014 08:44:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/15/renewing-self-signed-certificate-for-ejabberd/</link>
          <guid>https://artificialworlds.net/blog/2014/07/15/renewing-self-signed-certificate-for-ejabberd/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/15/renewing-self-signed-certificate-for-ejabberd/">&lt;p&gt;I run an &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.process-one.net&#x2F;en&#x2F;ejabberd&#x2F;&quot;&gt;ejabberd&lt;&#x2F;a&gt; server on an Ubuntu 12.10 box and this week I started getting notified by my IM client that the server&#x27;s certificate had expired.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I managed to generate a new certificate.&lt;&#x2F;p&gt;
&lt;p&gt;WARNING: this process backs up, deletes and then restores your ejabberd database, so it is probably fairly risky.&lt;&#x2F;p&gt;
&lt;pre&gt;# Move any previous backups out of the way
sudo mv &#x2F;var&#x2F;backups&#x2F;ejabberd-* ~&#x2F;Desktop&#x2F;

# Move the expired certificate out of the way
sudo mv &#x2F;etc&#x2F;ejabberd&#x2F;ejabberd.pem &#x2F;etc&#x2F;ejabberd&#x2F;ejabberd.pem.old

# Reconfigure the ejabberd package (WARNING: backs up and deletes your database!)
sudo dpkg-reconfigure ejabberd

# Make the database backup file readable
sudo chmod a+rx &#x2F;var&#x2F;backups&#x2F;ejabberd-*&#x2F;
sudo chmod a+r &#x2F;var&#x2F;backups&#x2F;ejabberd-*&#x2F;*

# Restore the backup
sudo ejabberdctl restore &#x2F;var&#x2F;backups&#x2F;ejabberd-*&#x2F;ejabberd-database
&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;re lucky, your server will now be back up with a new self-signed certificate.&lt;&#x2F;p&gt;
&lt;p&gt;In general, the policy of using dpkg-reconfigure to handle creating new self-signed certificate seems to work nicely.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jim
    &lt;span class=&quot;comment_date&quot;&gt;2015-09-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks.  That seems to have worked OK for me too.  However I’m not sure about the restoring of the database.  I get a file not found error at that stage, but I’m not clear what you’re restoring.&lt;br&#x2F;&gt;
Are you restoring the database you just created, or should that last line reference the stuff you backed up to ~&#x2F;Desktop?&lt;br&#x2F;&gt;
I have a working system so I’m not &#x2F;too&#x2F; concerned :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-09-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Jim, that should be restoring the database you just created.  Moving stuff to Desktop is just getting any old backups out of the way.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy
    &lt;span class=&quot;comment_date&quot;&gt;2015-10-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Why not use the routine creating the cert? In postinstall of debian ejabberd package it is (modified with the remove part):&lt;&#x2F;p&gt;
&lt;p&gt;$: cat &#x2F;root&#x2F;scripts&#x2F;regenerate-ejabberd.pem.sh&lt;br&#x2F;&gt;
#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;h1&gt;script to generate new ejabberd.pem&lt;&#x2F;h1&gt;
&lt;h1&gt;Making &#x2F;etc&#x2F;ejabberd&#x2F;ejabberd.pem if it does not exist.&lt;&#x2F;h1&gt;
&lt;p&gt;cd &#x2F;etc&#x2F;ejabberd&lt;br&#x2F;&gt;
if [ -f ejabberd.pem ]&lt;br&#x2F;&gt;
rm ejabberd.pem&lt;br&#x2F;&gt;
fi&lt;br&#x2F;&gt;
if [ ! -f ejabberd.pem ]&lt;br&#x2F;&gt;
then&lt;br&#x2F;&gt;
echo &quot;Generating SSL certificate &#x2F;etc&#x2F;ejabberd&#x2F;ejabberd.pem…&quot;&lt;br&#x2F;&gt;
EHOSTNAME=$(hostname -s 2&amp;gt;&#x2F;dev&#x2F;null || echo localhost)&lt;br&#x2F;&gt;
EDOMAINNAME=$(hostname -d 2&amp;gt;&#x2F;dev&#x2F;null || echo localdomain)&lt;br&#x2F;&gt;
openssl req -new -x509 -days 365 -nodes -out ejabberd.pem &lt;br&#x2F;&gt;
-keyout ejabberd.pem &amp;gt; &#x2F;dev&#x2F;null 2&amp;gt;&amp;amp;1 &amp;lt; last line is fi) and then make it executeable with chmod a+x&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-10-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Andy, good suggestion.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Christian
    &lt;span class=&quot;comment_date&quot;&gt;2020-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This no longer works. Is there an update on how to do it with the latest ejabberd available with raspbian?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jeff Cook
    &lt;span class=&quot;comment_date&quot;&gt;2014-09-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Worked for me! :) Thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Letter to my MP on DRIP</title>
          <pubDate>Mon, 14 Jul 2014 10:19:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/14/letter-to-my-mp-on-drip/</link>
          <guid>https://artificialworlds.net/blog/2014/07/14/letter-to-my-mp-on-drip/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/14/letter-to-my-mp-on-drip/">&lt;p&gt;Sent via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.writetothem.com&#x2F;&quot;&gt;WriteToThem.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gigaom.com&#x2F;2014&#x2F;07&#x2F;11&#x2F;lawyers-and-web-experts-attack-uks-fast-tracking-of-surveillance-legislation&#x2F;&quot;&gt;More Info&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Dear Mr Hammond,&lt;&#x2F;p&gt;
&lt;p&gt;I am writing to express my concern about the Data Retention and Investigatory Powers Bill (DRIP).&lt;&#x2F;p&gt;
&lt;p&gt;I feel that forcing companies to store data on their customers is an unacceptable breach of the right to privacy, and not justified by the need to combat terrorism or other crime. I think a better balance could be struck by requiring agencies to get a court order to engage in such invasive surveillance.&lt;&#x2F;p&gt;
&lt;p&gt;I am also concerned about the elements of DRIP that amend the Regulation of Investigatory Powers Act (RIPA), including the expansion of the definition of a communications service provider.&lt;&#x2F;p&gt;
&lt;p&gt;What emergency are we facing that requires parliament to move so quickly on this issue? The only emergency I can see is that UK government agencies are currently acting illegally.&lt;&#x2F;p&gt;
&lt;p&gt;These agencies should stop acting illegally, and laws should be drafted in the normal way to authorise proportionate surveillance.&lt;&#x2F;p&gt;
&lt;p&gt;I am particularly concerned that DRIP contains many similarities to legislation that has already been rejected by parliament.&lt;&#x2F;p&gt;
&lt;p&gt;I look forward to your response.&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
&lt;p&gt;[Inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;leomca.github.io&#x2F;2014&#x2F;07&#x2F;13&#x2F;Letter-to-MP-DRIP.html&quot;&gt;Leo McArdle&lt;&#x2F;a&gt;, thanks.]&lt;&#x2F;p&gt;
&lt;p&gt;[Feel free to re-use in whole or in part.]&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Chris
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I hope you don’t mind but I’v copied your letter and sent it to my MP.  Thank you.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Really glad to hear it was helpful!  I’ve added a comment making it explicit you can copy it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>What is node.js?</title>
          <pubDate>Mon, 14 Jul 2014 07:21:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/14/what-is-node-js/</link>
          <guid>https://artificialworlds.net/blog/2014/07/14/what-is-node-js/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/14/what-is-node-js/">&lt;p&gt;Node.js is a way of writing your web applications wholly in JavaScript, which sounds like a terrible idea.&lt;&#x2F;p&gt;
&lt;p&gt;However, it is also an asynchronous programming environment that has been proven to scale really well, and it&#x27;s a collection of libraries many of which are excitingly small and well-encapsulated.&lt;&#x2F;p&gt;
&lt;p&gt;Here we just cover the basics of what it is:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;What is node.js?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;45facd7a-377e-4c01-b629-5944decf922b&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;what-is-node-js&#x2F;what-is-node-js.html&quot;&gt;What is node.js?&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A mini mocking framework</title>
          <pubDate>Mon, 07 Jul 2014 07:18:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/07/07/a-mini-mocking-framework/</link>
          <guid>https://artificialworlds.net/blog/2014/07/07/a-mini-mocking-framework/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/07/07/a-mini-mocking-framework/">&lt;p&gt;Ever wondered how a mocking framework like JMock or GoogleMock works?&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll look at how to write a very basic example, and find out that Java is more flexible than you may have thought.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;A mini mocking framework&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b46a7f78-64ca-4d60-8265-e16a5afb8b8f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;mini-mocking&#x2F;mini-mocking.html&quot;&gt;A mini mocking framework&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Simple template programming</title>
          <pubDate>Mon, 30 Jun 2014 07:16:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/06/30/simple-template-programming/</link>
          <guid>https://artificialworlds.net/blog/2014/06/30/simple-template-programming/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/06/30/simple-template-programming/">&lt;p&gt;C++ template meta-programming sounds harder than it is.&lt;&#x2F;p&gt;
&lt;p&gt;So long as you can fight through some horrific syntax and understand recursive functions, you can write any algorithm you like and have it run at compile time in your C++ programs.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Simple Template Programming&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fd4637d0-f6a9-4f40-be25-257c87564314&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;simple-template-programming&#x2F;simple-template-programming.html&quot;&gt;Simple Template Programming&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Andrei Alexandrescu&#x27;s amazing book on using template meta-programming for really useful, cool stuff is: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;erdani.com&#x2F;index.php&#x2F;books&#x2F;modern-c-design&#x2F;&quot;&gt;Modern C++ Design&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Five Quines video</title>
          <pubDate>Wed, 25 Jun 2014 07:34:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/06/25/five-quines-video/</link>
          <guid>https://artificialworlds.net/blog/2014/06/25/five-quines-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/06/25/five-quines-video/">&lt;p&gt;A quine is a program that prints out its own source code. I will describe five examples:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Five Quines&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;fd5a12e0-0681-4377-bd34-1a2584bfa3e6&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;five-quines&#x2F;five-quines.html&quot;&gt;Five Quines&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Arguably the greatest program ever written:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mamememo.blogspot.co.uk&#x2F;2010&#x2F;09&#x2F;qlobe.html&quot;&gt;Qlobe&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;More info on quines:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.madore.org&#x2F;~david&#x2F;computers&#x2F;quine.html&quot;&gt;http:&#x2F;&#x2F;www.madore.org&#x2F;~david&#x2F;computers&#x2F;quine.html&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quine_%28computing%29&quot;&gt;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Quine_%28computing%29&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;rosettacode.org&#x2F;wiki&#x2F;Quine&quot;&gt;http:&#x2F;&#x2F;rosettacode.org&#x2F;wiki&#x2F;Quine&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;c2.com&#x2F;cgi&#x2F;wiki?QuineProgram&quot;&gt;http:&#x2F;&#x2F;c2.com&#x2F;cgi&#x2F;wiki?QuineProgram&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;update:&lt;&#x2F;strong&gt; just, wow: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;taylorconor&#x2F;quinesnake&quot;&gt;github.com&#x2F;taylorconor&#x2F;quinesnake&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>OpenID security in a REST API</title>
          <pubDate>Mon, 23 Jun 2014 07:21:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/06/23/openid-security-in-a-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2014/06/23/openid-security-in-a-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/06/23/openid-security-in-a-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, OpenID.&lt;&#x2F;p&gt;
&lt;p&gt;We are implementing a REST API. Today, using OpenID to do our authentication, instead of doing it ourselves:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;OpenID security in a REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6327946f-5d5b-4f25-85d1-251954c5cced&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;rest-security-openid&#x2F;rest-security-openid.html&quot;&gt;OpenID security in a REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>avconv performance slow in Ubuntu 14.04 trusty</title>
          <pubDate>Thu, 15 May 2014 00:50:02 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/05/15/avconv-performance-slow-in-ubuntu-14-04-trusty/</link>
          <guid>https://artificialworlds.net/blog/2014/05/15/avconv-performance-slow-in-ubuntu-14-04-trusty/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/05/15/avconv-performance-slow-in-ubuntu-14-04-trusty/">&lt;p&gt;I noticed that my TV conversion scripts were suddenly running very slowly after upgrading to Lubuntu 14.04.&lt;&#x2F;p&gt;
&lt;p&gt;After some investigation it turned out it had started defaulting to using libx264 encoding when the output file was named *.mp4.&lt;&#x2F;p&gt;
&lt;p&gt;When I ask it to use mpeg4 encoding instead, it goes fast again:&lt;&#x2F;p&gt;
&lt;pre&gt;avconv -i MyProg.ts -acodec copy -b:v 2500 &lt;span style=&quot;color:red;&quot;&gt;-c:v mpeg4&lt;&#x2F;span&gt; MyProg.mp4&lt;&#x2F;pre&gt;
&lt;p&gt;I assume in previous releases it was defaulting to mpeg4, unless there&#x27;s been some huge performance bug introduced into libx264.&lt;&#x2F;p&gt;
&lt;p&gt;Interestingly, when I built the latest version of avconv from their git repo, it didn&#x27;t include libx264 at all, and happily defaulted to mpeg4.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Does test-driven development harm clarity?</title>
          <pubDate>Fri, 09 May 2014 13:57:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/05/09/does-test-driven-development-harm-clarity/</link>
          <guid>https://artificialworlds.net/blog/2014/05/09/does-test-driven-development-harm-clarity/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/05/09/does-test-driven-development-harm-clarity/">&lt;p&gt;In a recent keynote at RailsConf called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.confreaks.com&#x2F;videos&#x2F;3315-railsconf-keynote&quot;&gt;Writing Software&lt;&#x2F;a&gt;*, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;david.heinemeierhansson.com&#x2F;&quot;&gt;David Heinemeier Hansson&lt;&#x2F;a&gt; argues that test-driven development (TDD) can harm the clarity of our code, and that clarity is the key thing we should be aiming for when writing software.&lt;&#x2F;p&gt;
&lt;p&gt;(*contains swearing)&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s an excellent talk, and I would highly recommend watching it, especially if you are convinced (like me) that TDD is a good thing.&lt;&#x2F;p&gt;
&lt;p&gt;I was inspired by watching the video. Clarity certainly is extremely important, and the name he coins, Software Writer, sits better with me than Software Engineer or Software Developer. I have often felt Programmer was the best name for what I am, but maybe I will adopt Software Writer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-real-goal&quot;&gt;The real goal&lt;&#x2F;h3&gt;
&lt;p&gt;I would argue that clarity is not our goal in writing software. I think our goal is:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Working, modifiable software&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Clarity helps us feel confident that our software works because we can read the code and understand what it does.&lt;&#x2F;p&gt;
&lt;p&gt;Clarity helps us modify our software because we can understand what needs to be changed and are less likely to make mistakes when we change it.&lt;&#x2F;p&gt;
&lt;p&gt;A good set of full-system-level tests helps us feel confident that our software works because they prove it works in certain well-defined scenarios. A good set of component-level and unit tests gives us confidence that various parts work, but as David points out, confidence in these separate parts does not give us much meaningful confidence that the whole system works.&lt;&#x2F;p&gt;
&lt;p&gt;Good sets of tests at all levels help us modify our software because we are free to refactor (or re-draft as David puts it). Unit and component tests give us confidence that structural changes we are making don&#x27;t modify the external behaviour of the part we are re-structuring. Once we have made enabling structural changes, the changes we make that actually modify the system&#x27;s behaviour are smaller and easier to deal with. The tests that break or must be written when we modify the system&#x27;s behaviour help us understand and explain the behaviour changes we are making.&lt;&#x2F;p&gt;
&lt;p&gt;So both clarity and tests at all levels can contribute to our goal of writing working, modifiable software.&lt;&#x2F;p&gt;
&lt;p&gt;But David wasn&#x27;t talking about tests - he was talking about TDD - driving the design of software by writing tests.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-tdd-encourages-clarity&quot;&gt;How TDD encourages clarity&lt;&#x2F;h3&gt;
&lt;p&gt;Before I talk about how we should accept some of what David is saying, let&#x27;s first remind ourselves of some counter-points. TDD is explicitly intended to improve our code.&lt;&#x2F;p&gt;
&lt;p&gt;I agree with David when he defines good code as clear code, so how does TDD encourage clarity?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TDD encourages us to break code into smaller parts, with names.&lt;&#x2F;strong&gt; Smaller, named classes and functions are clearer to me than large blocks of code containing sub-blocks that do specific jobs but are not named. When I write in a TDD style I find it encourages me to break code into smaller parts.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TDD encourages us to write code that works at a single level of abstraction.&lt;&#x2F;strong&gt; Code that mixes different levels is less clear than code at a single level. I find that using TDD helps me resist the temptation to mix levels because it encourages me to compose two pieces that deal separately with each level, rather than linking them together.&lt;&#x2F;p&gt;
&lt;p&gt;It is vital to point out here that TDD doesn&#x27;t push you towards clarity unless you already wanted to go there. I have seen TDD code that is unclear, stuffed full of boilerplate, formed by copy-paste repetition, and is generally awful. As a minimal counter-example, TDD doesn&#x27;t stop you using terrible names for things.&lt;&#x2F;p&gt;
&lt;p&gt;But, when you care about clarity, and have an eye for it, I feel TDD can help you achieve it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-tdd-hurts-clarity&quot;&gt;How TDD hurts clarity&lt;&#x2F;h3&gt;
&lt;p&gt;David&#x27;s argument against TDD is that it makes us write code that is less clear. His main argument, as I understand it, is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;TDD forces us to use unnecessary layers of abstraction.&lt;&#x2F;strong&gt; Because we must never depend on &quot;the world,&quot; TDD forces us to inject dependencies at every level. This makes our code more complex and less clear.&lt;&#x2F;p&gt;
&lt;p&gt;At its core, we must acknowledge that this argument is true. Where TDD causes us to inject dependencies that we otherwise would not inject, we are making our code more complex.&lt;&#x2F;p&gt;
&lt;p&gt;However, there are elements of a straw man here too. Whenever I can, I allow TDD to drive me towards systems with fewer dependencies, not injected dependencies. When I see a system with fewer dependencies, I almost always find it clearer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;test-against-the-real-database&quot;&gt;Test against the real database?&lt;&#x2F;h3&gt;
&lt;p&gt;David frequently repeats his example of testing without hitting the database. He points out that allowing this increases complexity, and that the resulting tests do not have anything like as much value as tests that do use the database.&lt;&#x2F;p&gt;
&lt;p&gt;This hurts, because I think his point is highly valid. I have seen lots of bugs, throughout systems (not just in code close to the database) that came from wrong assumptions about how the database would behave. Testing a wide range of functionality against the real database seems to be the only answer to this problem. Even testing against a real system that is faster (e.g. an in-memory database) will not help your discover all of these bugs because the faster database will have different behaviour from the production one.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, tests that run against a real database will be too slow to run frequently during development, slowing everything down and reducing the positive effects of TDD.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know what the answer is, but part of it has got to be to write tests at component level, testing all the behaviour that is driven by database behaviour, but not requiring huge amounts of other systems to be spun up (e.g. the web server, LDAP, other HTTP endpoints) and run these against the real database as often as possible. If they only take about 5 minutes maybe it&#x27;s reasonable to ask developers to run them before they commit code.&lt;&#x2F;p&gt;
&lt;p&gt;But my gut tells me that running tests at this level should not absolve us from abstracting our code from the production database. It just feels Right to write code that works with different storage back ends. We are very likely to have to change the specific database we use several times in the history of our code, and we may well need to change the paradigm e.g. NoSQL-&amp;gt;SQL.&lt;&#x2F;p&gt;
&lt;p&gt;In a component that is based on the database, I think you should unit test the logic in the standard TDD way, unit test the database code (e.g. code that generates SQL statements) against a fast fake database, AND comprehensively test the behaviour of the component as a whole against a real database. I admit this looks like a lot of tests, but if you avoid &quot;dumb&quot; unit tests that e.g. check getters and setters, I think these 3 levels have 3 different purposes, and all have value*.&lt;&#x2F;p&gt;
&lt;p&gt;*Writing the logic TDD encourages smaller units of logic, and gives confidence that it is correct. Testing the database code against a fake database gives confidence that our syntax is right, and testing the whole component against the real database gives us confidence that our behaviour really works.&lt;&#x2F;p&gt;
&lt;p&gt;Injecting the database as a dependency gives us the advantage of our code having two consumers, which is one of the strongest arguments put forward by proponents of TDD that it gives us better code. All programmers know that there are only three numbers: 0, 1 and more. By having &quot;more&quot; users of our database code, we (hopefully) end up with code that speaks at a single level e.g. there are no SQL statements peppered around code which has no business talking direct to the database.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clarity-of-tests&quot;&gt;Clarity of tests&lt;&#x2F;h3&gt;
&lt;p&gt;In order for tests to drive good APIs in our production code, and for them to serve as documentation, they must be clear. I see a lot of test code that is full of repetition and long methods, and for me this makes it much less useful.&lt;&#x2F;p&gt;
&lt;p&gt;If our tests are complex and poorly factored, they won&#x27;t drive good practice in our production code. If we view unclear tests as a smell, the fixes we make will often encourage us to improve the clarity of our production code.&lt;&#x2F;p&gt;
&lt;p&gt;If our tests resemble (or actually are) automatically-generated dumb callers of each method, we will have high coverage and almost no value from them. If we try to change code that is tested in this way, we will be thwarted at every turn.&lt;&#x2F;p&gt;
&lt;p&gt;If, on the other hand, we write tests that are clear and simple expressions of behaviour we expect, we will find them easy to understand and maintain, they will drive clear code in production, and sometimes we may realise we are writing them at a higher level than unit tests. When this happens, we should &quot;float freely&quot; with David and embrace that. They are testing more. That is good.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;higher-level-tests&quot;&gt;Higher-level tests&lt;&#x2F;h3&gt;
&lt;p&gt;David (with the support of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sites.google.com&#x2F;a&#x2F;gertrudandcope.com&#x2F;www&#x2F;jimcoplien&quot;&gt;Jim Coplien&lt;&#x2F;a&gt;) encourages us to test at a coarser grain than the unit. I strongly agree that we need more emphasis on testing at the component and system levels, and sometimes less unit testing, since we don&#x27;t want write two tests that test the same thing.&lt;&#x2F;p&gt;
&lt;p&gt;However, there are some problems with larger tests.&lt;&#x2F;p&gt;
&lt;p&gt;First, larger tests make it difficult to identify what caused a problem. When a good unit test fails, the problem we have introduced (or the legitimate change in behaviour) is obvious. When a component or system test fails, often all we know is that something is wrong, and the dreaded debugging process must begin. In my experience, this is not just a myth. One of the pieces of code that I am most proud of in my career was a testing framework allowed you to write concise and clear tests of a horrible tangled mess of a component. The tests ran fairly quickly, with no external dependencies being used, but if one of them failed your heart sank.&lt;&#x2F;p&gt;
&lt;p&gt;Second, large tests can be fragile. Sometimes they fail because the database or network is down. Sometimes they fail because your threads did stuff in an unexpected order. Threading is a really good example: when I want to debug a weird threading issue I want to write an unthreaded test that throws stuff at a component in a well-defined but unusual order. When I can get that test to fail I know I&#x27;ve found a real problem, and I can fix it. If the code is not already unit tested (with dependencies being injected, sometimes) then writing that test can be really difficult.&lt;&#x2F;p&gt;
&lt;p&gt;TDD makes our code testable, and while testability (like clarity) is not an end in itself, it can be darn useful.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;good-things&quot;&gt;Good things&lt;&#x2F;h4&gt;
&lt;p&gt;Clarity is good because it supports working, modifiable code.&lt;&#x2F;p&gt;
&lt;p&gt;Tests are good because they support working, modifiable code.&lt;&#x2F;p&gt;
&lt;p&gt;Testability is good because it supports tests, especially debugging tests.&lt;&#x2F;p&gt;
&lt;p&gt;TDD is good when it supports clarity, testability and tests.&lt;&#x2F;p&gt;
&lt;p&gt;TDD is bad when it hurts clarity.&lt;&#x2F;p&gt;
&lt;p&gt;If you throw out TDD, try not to throw out tests or testability. You will regret it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;what-to-do&quot;&gt;What to do&lt;&#x2F;h4&gt;
&lt;p&gt;Write tests at the right level. Don&#x27;t worry if the clearest level is not the unit level.&lt;&#x2F;p&gt;
&lt;p&gt;Use tests to improve clarity.&lt;&#x2F;p&gt;
&lt;p&gt;If your tests are unclear, there is something wrong. Fix it.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;steps-to-success&quot;&gt;Steps to success&lt;&#x2F;h4&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Get addicted to TDD.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Wean yourself off TDD and start to look at the minimal set of tests you can write to feel that sweet, sweet drug of confidence.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Do not skip step 1.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2014-05-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;As I was reading this this morning I just got to the part about testing against &quot;real&quot; databases when I finished my breakfast and got back to work.&lt;br&#x2F;&gt;
The task in progress was investigating a bug that only happens when we connect to the real database. Ha!&lt;br&#x2F;&gt;
The trouble is all our dev databases are currently being refreshed (with data from production) so I can’t work on the issue for an hour or so at this point – and when I can there’s no guarantee that the data that caused the issue will be the same!&lt;br&#x2F;&gt;
D’oh!&lt;br&#x2F;&gt;
In our case I believe it’s the difference in the data that caused the issue, rather than the database itself. This is why we run higher level integration and system regression test suites against the dev DBs at least nightly (and where this showed up). But once t does show up I’ll try and capture it in a deterministic unit test (or low-level integration test – by capturing a subset of the DB data).&lt;br&#x2F;&gt;
Clearly both are needed – for all sorts of reasons. But it’s primarily the unit tests that drive the low-level design. System tests drive the high-level design (as they show how our code behaves when it hits &quot;the real world&quot;. There’s a dance between these levels as we try to capture things we learn at the system test level back at the unit test level (where appropriate).&lt;br&#x2F;&gt;
In short TDD is driven by tests. Which level those tests are pitched at varies as appropriate to the project, time etc – but you typically need multiple levels that all influence the design in some respect. (I’d also say that a lot of what I label &quot;unit tests&quot; in our code are really low-level integration tests – but they largely meet Michael Feathers’ requirements for unit tests, so I continue to call them that).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-05-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Definitely some of my best tests have been &quot;component&quot; tests – things that check real behaviour of relatively large areas of code, but still satisfy Feathers’ requirements for unit tests.&lt;&#x2F;p&gt;
&lt;p&gt;Also, I think driving high-level design from high-level tests and low-level design from low-level tests is the right way to think about things.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Token-based security in a REST API</title>
          <pubDate>Tue, 29 Apr 2014 07:20:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/04/29/token-based-security-in-a-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2014/04/29/token-based-security-in-a-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/04/29/token-based-security-in-a-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, Token, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We are implementing a REST API. Today, adding to the security we already have by making a security token that is stored in a cookie:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Token-based security in a REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;37f58bc7-6b1a-415c-b3c6-8ce3aa55cb83&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;rest-security-token&#x2F;rest-security-token.html&quot;&gt;Token-based security in a REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dieu Merci
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Good evening Andy, I have some problem to understand Rest security. I don’t know if you can help me. Firstly I’m confuse for making the different between API and the Backend, and secondly I don’t know what points to concern the  Rest API security. By following your video you focused on authentication and autorization, my question for this is what about injections, CSRF, XSS, …&lt;&#x2F;p&gt;
&lt;p&gt;Thanks.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Dieu Merci, can you ask a more specific question?  I don’t know how to answer this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dieu Merci
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;how to protect an Rest API from Sql injections, CSRF attack?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I suggest looking at &lt;a href=&quot;https:&#x2F;&#x2F;www.owasp.org&#x2F;index.php&#x2F;Category:OWASP_Top_Ten_Project&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.owasp.org&#x2F;index.php&#x2F;Category:OWAS...&lt;&#x2F;a&gt; – the security aspects of a REST API are similar to other projects, except that there is usually no JavaScript running in the process.  However, often the results of an API request are processed inside JavaScript, so many of the same ideas are relevant.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Security in a REST API – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Series: What is REST?, A Basic REST API, Paging, Search, Security, Token. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Avoid mocks by refactoring to functional</title>
          <pubDate>Fri, 11 Apr 2014 16:23:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/04/11/avoid-mocks-by-refactoring-to-functional/</link>
          <guid>https://artificialworlds.net/blog/2014/04/11/avoid-mocks-by-refactoring-to-functional/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/04/11/avoid-mocks-by-refactoring-to-functional/">&lt;p&gt;At this week&#x27;s &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;&quot;&gt;ACCU&lt;&#x2F;a&gt; Conference I went to an excellent talk by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;rosvall.ie&#x2F;Sven-E.html&quot;&gt;Sven Rosvall&lt;&#x2F;a&gt; entitled &quot;Unit Testing Beyond Mock Objects&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The talk covered the newer Java and C# unit testing frameworks that allow inserting mock objects even where legacy code is using some undesired dependency directly, meaning there is no &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;c2.com&#x2F;cgi&#x2F;wiki?SoftwareSeam&quot;&gt;seam&lt;&#x2F;a&gt; where you can insert a different implementation.&lt;&#x2F;p&gt;
&lt;p&gt;These tools solve a real problem, and could be useful.&lt;&#x2F;p&gt;
&lt;p&gt;However, I want to move the discussion in a different direction: can we avoid mocking altogether, and end up with better code?&lt;&#x2F;p&gt;
&lt;p&gt;Sven gave us an example of some legacy code a little bit like this. (I translated into C++ just for fun.)&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; Return true if it&#x27;s between 12:00 and 14:00 now.
bool is_it_lunch_time_now()
{
    system_clock::time_point now = system_clock::now();
    time_t tt = system_clock::to_time_t( now );
    tm local_tm = ( *localtime( &amp;amp;tt ) );
    int hour = local_tm.tm_hour;

    return (
        12 &amp;lt;= hour      &amp;amp;&amp;amp;
              hour &amp;lt; 14
    );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;To test this code, we would have something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;TEST_CASE( &quot;Now: It&#x27;s lunchtime now (hopefully)&quot; )
{
    &#x2F;&#x2F; REQUIRE( is_it_lunch_time_now() ); &#x2F;&#x2F; NOTE: only  run at lunch time!
    REQUIRE( !is_it_lunch_time_now() );   &#x2F;&#x2F; NOTE: never run at lunch time!
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Which is agony. But the production code is fine:&lt;&#x2F;p&gt;
&lt;pre&gt;if ( is_it_lunch_time_now() )
{
    eat( sandwich );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;So, the normal way to allow mocking out a dependency like this would be to add an interface, making our lunch-related code take in a TimeProvider. To avoid coupling the choice of which TimeProvider is used to the calling code, we pass it into the constructor of a LunchCalculator that we plan to make early on:&lt;&#x2F;p&gt;
&lt;pre&gt;class TimeProvider
{
public:
    virtual tm now_local() const = 0;
};

class RealTimeProvider : public TimeProvider
{
public:
    RealTimeProvider()
    {
    }
    virtual tm now_local() const
    {
        system_clock::time_point now = system_clock::now();
        time_t tt = system_clock::to_time_t( now );
        return ( *localtime( &amp;amp;tt ) );
    }
};

class HardCodedHourTimeProvider : public TimeProvider
{
public:
    HardCodedHourTimeProvider( int hour )
    {
        tm_.tm_hour = hour;
    }
    virtual tm now_local() const
    {
        return tm_;
    }
private:
    tm tm_;
};

class LunchCalc
{
public:
    LunchCalc( const TimeProvider&amp;amp; prov )
    : prov_( prov )
    {
    }
    bool is_it_lunch_time()
    {
        int hour = prov_.now_local().tm_hour;
        return (
            12 &amp;lt;= hour      &amp;amp;&amp;amp;
                  hour &amp;lt; 14
        );
    }
private:
    const TimeProvider&amp;amp; prov_;
};
&lt;&#x2F;pre&gt;
&lt;p&gt;and now we can write tests like this:&lt;&#x2F;p&gt;
&lt;pre&gt;TEST_CASE( &quot;TimeProvider: Calculate lunch time when it is&quot; )
{
    HardCodedHourTimeProvider tp( 13 ); &#x2F;&#x2F; 1pm (lunch time!)
    REQUIRE( LunchCalc( tp ).is_it_lunch_time() );
}

TEST_CASE( &quot;TimeProvider: Calculate lunch time when it isn&#x27;t&quot; )
{
    HardCodedHourTimeProvider tp( 10 ); &#x2F;&#x2F; 10am (not lunch :-( )
    REQUIRE( ! LunchCalc( tp ).is_it_lunch_time() );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Innovatively, these tests will pass at all times of day.&lt;&#x2F;p&gt;
&lt;p&gt;However, look at the price we&#x27;ve had to pay: 4 new classes, inheritance, and class names ending with un&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Kevlin_Henney&quot;&gt;Kevlin&lt;&#x2F;a&gt;ic words like &quot;Provider&quot; and &quot;Calculator&quot;. (Even worse, in a vain attempt to hide my embarrassment I abbreviated to &quot;Calc&quot;.)&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve paid a bit of a price in our production code too:&lt;&#x2F;p&gt;
&lt;pre&gt;    &#x2F;&#x2F; Near the start of the program:
    config.time_provider = RealTimeProvider();

    &#x2F;&#x2F; Later, with config passed via &lt;a href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;1411&quot;&gt;PfA&lt;&#x2F;a&gt;
    if ( LunchCalc( config.time_provider ).is_it_lunch_time() )
    {
        eat( sandwich );
    }
&lt;&#x2F;pre&gt;
&lt;p&gt;The code above where we create the RealTimeProvider is probably not usefully testable, and the class RealTimeProvider is also probably not usefully testable (or safely testable in the case of some more dangerous dependency). The rest of this code is testable, but there is a lot of it, isn&#x27;t there?&lt;&#x2F;p&gt;
&lt;p&gt;The advantage of this approach is that we have been driven to a better structure. We can now switch providers on startup by providing config, and even code way above this stuff can be fully exercised safe in the knowledge that no real clocks were poked at any time.&lt;&#x2F;p&gt;
&lt;p&gt;But. Sometimes don&#x27;t you ever think this might be better?&lt;&#x2F;p&gt;
&lt;pre&gt;tm tm_local_now()  &#x2F;&#x2F; Not testable
{
    system_clock::time_point now = system_clock::now();
    time_t tt = system_clock::to_time_t( now );
    return ( *localtime( &amp;amp;tt ) );
}

bool is_lunch_time( const tm&amp;amp; time )  &#x2F;&#x2F; Pure functional - eminently testable
{
    int hour = time.tm_hour;
    return (
        12 &amp;lt;= hour      &amp;amp;&amp;amp;
              hour &amp;lt; 14
    );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;The tests look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;TEST_CASE( &quot;Functional: Calculate lunch time when it is&quot; )
{
    tm my_tm;
    my_tm.tm_hour = 13; &#x2F;&#x2F; 1pm (lunch time!)
    REQUIRE( is_lunch_time( my_tm ) );
}

TEST_CASE( &quot;Functional: Calculate lunch time when it isn&#x27;t&quot; )
{
    tm my_tm;
    my_tm.tm_hour = 10; &#x2F;&#x2F; 10am (not lunch time :-( )
    REQUIRE( ! is_lunch_time( my_tm ) );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;and the production code looks very similar to what we had before:&lt;&#x2F;p&gt;
&lt;pre&gt;if ( is_lunch_time( tm_local_now() ) )
{
    eat( sandwich );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Where possible, let&#x27;s not mock stuff we can just test as pure, functional code.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the production code shown here is now untestable, so there may well be work needed to avoid calling it in a test. That work may involve a mock. Or we may find a nicer way to avoid it.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Peter Bindels
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;… What about just mocking what you use? Stop trying to make C++ into Java with EasyMock or C# with RhinoMock, use something tailored to C++ (and I mean all of C++).&lt;&#x2F;p&gt;
&lt;p&gt;#include &quot;hippomocks.h&quot;&lt;&#x2F;p&gt;
&lt;p&gt;TEST_CASE( &quot;Calculate lunch time when it is&quot; )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
MockRepository mocks;&lt;br&#x2F;&gt;
mocks.ExpectCall(system_clock::now).Return(system_clock::lunchtime);&lt;br&#x2F;&gt;
REQUIRE( is_lunch_time() );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;TEST_CASE( &quot;Calculate lunch time when it isn’t&quot; )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
MockRepository mocks;&lt;br&#x2F;&gt;
mocks.ExpectCall(system_clock::now).Return(system_clock::dinnertime);&lt;br&#x2F;&gt;
REQUIRE( ! is_lunch_time() );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;Stop using C++ as if it’s a badly implemented Java or a badly implemented Haskell. Use it for what it’s worth.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    GermÃ¡n
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have been thinking on how to mock avoiding to,write an interface.&lt;&#x2F;p&gt;
&lt;p&gt;You could do this, though it is not superior to the last solution:&lt;&#x2F;p&gt;
&lt;p&gt;extern std::function is_it_lunch_time_now;&lt;&#x2F;p&gt;
&lt;p&gt;Default implementation goes in .cpp.&lt;&#x2F;p&gt;
&lt;p&gt;In test code:&lt;&#x2F;p&gt;
&lt;p&gt;is_lunch_time_now = &#x2F;&lt;em&gt;mock impl&lt;&#x2F;em&gt;&#x2F;;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    GermÃ¡n
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;function is bool() but the website swallowed things&lt;br&#x2F;&gt;
between less than and greater than symbols&lt;br&#x2F;&gt;
due to markup I guess.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    German Diago
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You can avoid writing an interface doing this, but needs source code modification:&lt;&#x2F;p&gt;
&lt;p&gt;extern std::function is_it_lunch_time_now;&lt;&#x2F;p&gt;
&lt;p&gt;.cpp file:&lt;&#x2F;p&gt;
&lt;p&gt;is_it_lunch_time_now = []() -&amp;gt; bool { … };&lt;&#x2F;p&gt;
&lt;p&gt;test file, rebind function:&lt;&#x2F;p&gt;
&lt;p&gt;is_it_lunch_time_now = &#x2F;&lt;em&gt;mock implementation&lt;&#x2F;em&gt;&#x2F;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Peter: it looks like hoppomocks gives you the ability to insert a seam where there isn’t one, like PowerMock which was the subject of the talk that inspired this post.  This sounds like a helpful thing to be able to do when you’re in a situation where there is a lot of code already using untestable code, but I am trying to think about how we would strucure code ideally.  In my opinion, in the ideal case we wouldn’t rely on &quot;magic&quot; like that.  Of course, that argument relies on us sharing understanding of what is &quot;magic&quot; and what is perfectly good and normal code.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;GermÃ¡n: thank you, I fixed the less-thans.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;GermÃ¡n: yes, that is another way to avoid mocks.  But isn’t the functional style for is_lunch_time() nicer anyway, even if we also implement is_it_lunch_time_now() based on it?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Peter Bindels
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&amp;gt; Of course, that argument relies on us sharing understanding of what is â€œmagicâ€ and what is perfectly good and normal code.&lt;&#x2F;p&gt;
&lt;p&gt;To me it is the discussion of what exactly an interface is. Is it only an interface if you have a C++ class with pure virtual methods that you can replace with a mock object? Or is a set of C functions with a coherent goal also an interface?&lt;&#x2F;p&gt;
&lt;p&gt;I tend to the latter. Having code that does not require mocks to test is inherently better, but you will have to tie those bits of code together &lt;em&gt;somehow&lt;&#x2F;em&gt;, and you’ll need to check that the whole of those bits of code does what you expect it to do. You can check a key and a lock, but until you check them together you don’t know you have the &lt;em&gt;right&lt;&#x2F;em&gt; key.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Peter, yes – it is legitimate to use natural seams (e.g. a mock implementation that matches the declared function signature of the real dependency) that are available in C++ that are not in a language like Java.  However, I’d still prefer to write code whose behaviour depends only on its input where possible, and doesn’t use an untestable depenency call in its implementation.  I do admit you’ve suggested a good way to replace that dependency call, but I’d still rather not have it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;First – good to see you using Catch for your test framework :–)&lt;&#x2F;p&gt;
&lt;p&gt;Second – I completely agree with your thought processes here. It’s one of the reasons that truly functional code is so much easier to reason about (once you get your head around the paradigm in the first place) – but it all has it’s start in straightforward stuff like this.&lt;&#x2F;p&gt;
&lt;p&gt;Thirdly – despite a different language and focus – it echoes a lot of what I wrote here: &lt;a href=&quot;http:&#x2F;&#x2F;www.levelofindirection.com&#x2F;journal&#x2F;2013&#x2F;7&#x2F;11&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.levelofindirection.com&#x2F;journal&#x2F;201...&lt;&#x2F;a&gt; (which I know you’ve seen because you were the first commenter ;–) )&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Phil – yes, this was my first go with CATCH.  I enjoyed it.&lt;br&#x2F;&gt;
We are in total agreement :–) If only everyone listened to us …?&lt;br&#x2F;&gt;
(That link should be &lt;a href=&quot;http:&#x2F;&#x2F;www.levelofindirection.com&#x2F;journal&#x2F;2013&#x2F;7&#x2F;11&#x2F;injecting-singletons-in-objective-c-unit-tests.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.levelofindirection.com&#x2F;journal&#x2F;201...&lt;&#x2F;a&gt; )&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Using the final keyword in interface method parameters does nothing</title>
          <pubDate>Wed, 02 Apr 2014 09:13:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/04/02/using-the-final-keyword-in-interface-method-parameters-does-nothing/</link>
          <guid>https://artificialworlds.net/blog/2014/04/02/using-the-final-keyword-in-interface-method-parameters-does-nothing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/04/02/using-the-final-keyword-in-interface-method-parameters-does-nothing/">&lt;p&gt;Consider the following Java code:&lt;&#x2F;p&gt;
&lt;pre&gt;class FinalInInterface
{
    private static interface WithFinal
    {
        public void run( final int x );
    }

    private static class WithoutFinal implements WithFinal
    {
        public void run( int x )
        {
            x = 4;
            System.out.println( x );
        }
    }

    public static void main( String[] args )
    {
        new WithoutFinal().run( 3 );
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;This code compiles, and when it runs, it prints &quot;4&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;So adding &quot;final&quot; to the x parameter of the run() method in the interface WithFinal has no effect - the implementor of this interface, WithoutFinal is allowed to declare its own run() method without &quot;final&quot;, and modify x as much as they want.&lt;&#x2F;p&gt;
&lt;p&gt;This makes more sense if you realise that in Java &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.javacodegeeks.com&#x2F;2012&#x2F;04&#x2F;using-final-keyword-on-method.html&quot;&gt;every method argument is passed by value&lt;&#x2F;a&gt;. When you pass an object reference, the method and the calling code are talking about the same object, but the reference is passed by value, so if you reassign it inside the method, the original object is unaffected, and the reference to it in the calling code is also unaffected. The semantics are basically identical to passing a pointer in C or C++ - the pointer is copied, but the pointed-to object is the same.&lt;&#x2F;p&gt;
&lt;p&gt;If you declare method parameters final in your interface, people implementing that interface don&#x27;t need to declare them final, and can modify them in their implementations. It&#x27;s possible that by declaring them final you are trying to communicate to implementors of the interface that they should also declare them final, but as the designer of the interface, it&#x27;s really none of your business how the implementor implements it.&lt;&#x2F;p&gt;
&lt;p&gt;There is no &quot;const&quot; in Java, so you have no way of preventing the implementor of your interface from modifying an object that is passed in (by copying a reference to it).&lt;&#x2F;p&gt;
&lt;p&gt;If that makes you sad, join the club.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using xscreensaver to lock the screen in Lubuntu</title>
          <pubDate>Wed, 26 Mar 2014 09:17:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/03/26/using-xscreensaver-to-lock-the-screen-in-lubuntu/</link>
          <guid>https://artificialworlds.net/blog/2014/03/26/using-xscreensaver-to-lock-the-screen-in-lubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/03/26/using-xscreensaver-to-lock-the-screen-in-lubuntu/">&lt;p&gt;Recent versions of Lubuntu seem to have &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;lxsession&#x2F;+bug&#x2F;1205384&quot;&gt;broken screen locking&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To get screen locking via &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.jwz.org&#x2F;xscreensaver&#x2F;&quot;&gt;xscreensaver&lt;&#x2F;a&gt; do this:&lt;&#x2F;p&gt;
&lt;pre&gt;# Install xscreensaver
sudo apt-get install xscreensaver

# Start xscreensaver when you log in
echo &quot;xscreensaver -nosplash&quot; &amp;gt;&amp;gt; ~&#x2F;.config&#x2F;lxsession&#x2F;Lubuntu&#x2F;autostart

# Edit the &quot;screen lock&quot; launcher
sudo leafpad &#x2F;usr&#x2F;share&#x2F;applications&#x2F;lubuntu-screenlock.desktop
&lt;&#x2F;pre&gt;
&lt;p&gt;Change lubuntu-screenlock.desktop to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;[Desktop Entry]
Name=ScreenLock &lt;span style=&quot;color: red;&quot;&gt;(xscreensaver)&lt;&#x2F;span&gt;
Name[zh_TW]=éŽ–ä½èž¢å¹•
Comment=Lock your screen
Icon=system-lock-screen
Exec=&lt;span style=&quot;color: red;&quot;&gt;xscreensaver-command -lock&lt;&#x2F;span&gt;
NoDisplay=true
&lt;&#x2F;pre&gt;
&lt;p&gt;If you want a keyboard shortcut to lock the screen, add this entry (under another &amp;lt;keybind&amp;gt; tag) to ~&#x2F;.config&#x2F;openbox&#x2F;lubuntu-rc.xml:&lt;&#x2F;p&gt;
&lt;pre&gt;    &amp;lt;keybind key=&quot;C-A-l&quot;&amp;gt;
      &amp;lt;action name=&quot;Execute&quot;&amp;gt;
        &amp;lt;command&amp;gt;xscreensaver-command -lock&amp;lt;&#x2F;command&amp;gt;
      &amp;lt;&#x2F;action&amp;gt;
    &amp;lt;&#x2F;keybind&amp;gt;
&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Michael Day
    &lt;span class=&quot;comment_date&quot;&gt;2016-06-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Awesome.  This helped me a LOT today on Lubuntu 16.04.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks a ton.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-06-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Michael, I’m glad it helped!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Security in a REST API</title>
          <pubDate>Tue, 04 Mar 2014 18:09:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/03/04/security-in-a-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2014/03/04/security-in-a-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/03/04/security-in-a-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, Security, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We are implementing a REST API. Today, providing security using HTTP Basic Authorization:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Security in a REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1bc415ad-e381-4dbc-ae6c-d7f3665e8b4f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;rest-security-basic&#x2F;rest-security-basic.html&quot;&gt;Security in a REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Token-based security in a REST API – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] What is REST?, A Basic REST API, Paging, Search, Security, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Searching in a REST API</title>
          <pubDate>Fri, 31 Jan 2014 08:24:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2014/01/31/searching-in-a-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2014/01/31/searching-in-a-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2014/01/31/searching-in-a-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, Search, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We are implementing a REST API. Today, we provide a basic search.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Searching in a REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;9853008a-a344-49fe-88b9-26fe56f53f92&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;searching-in-a-rest-api&#x2F;searching-in-a-rest-api.html&quot;&gt;Searching in a REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Paging in a REST API – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2014-01-31&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] in the morning, still writing Free Software      « A Basic REST API Searching in a REST API […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    What is REST? – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2014-03-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] What is REST?, A Basic REST API, Paging, Search, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    A Basic REST API – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] What is REST?, A Basic REST API, Paging, Search, Security, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Paging in a REST API</title>
          <pubDate>Thu, 05 Dec 2013 08:26:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/12/05/paging-in-a-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2013/12/05/paging-in-a-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/12/05/paging-in-a-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, Paging, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We consider some choices of how to provide paging through a list resource in a REST API.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Paging in a REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;1bfdd6a8-aece-4b6b-af7c-5530e0d4956d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;paging-in-a-rest-api&#x2F;paging-in-a-rest-api.html&quot;&gt;Paging in a REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A Basic REST API</title>
          <pubDate>Wed, 06 Nov 2013 21:08:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/11/06/a-basic-rest-api/</link>
          <guid>https://artificialworlds.net/blog/2013/11/06/a-basic-rest-api/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/11/06/a-basic-rest-api/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;10&#x2F;01&#x2F;what-is-rest&#x2F;&quot;&gt;What is REST?&lt;&#x2F;a&gt;, A Basic REST API, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll walk you through the code of a simple REST API I am writing to be a kind of YouTube for poetry.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;A Basic REST API&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;820b31c8-0d12-4fb1-a1ba-20b989fe7ceb&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;a-basic-rest-api-reboot&#x2F;a-basic-rest-api-reboot.html&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Avoid backslashes anywhere in Java code (Java error &quot;illegal unicode escape&quot;)</title>
          <pubDate>Thu, 10 Oct 2013 10:16:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/10/10/avoid-backslashes-anywhere-in-java-code-java-error-illegal-unicode-escape/</link>
          <guid>https://artificialworlds.net/blog/2013/10/10/avoid-backslashes-anywhere-in-java-code-java-error-illegal-unicode-escape/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/10/10/avoid-backslashes-anywhere-in-java-code-java-error-illegal-unicode-escape/">&lt;p&gt;Did you know you can insert unicode-escaped characters, anywhere in a Java program?&lt;&#x2F;p&gt;
&lt;p&gt;Most of us are familiar with using unicode escapes like this:&lt;&#x2F;p&gt;
&lt;pre&gt;String pound = &quot;\u00A3&quot;;&lt;&#x2F;pre&gt;
&lt;p&gt;but in fact constructs like \u00A3 can go anywhere, including in a comment.&lt;&#x2F;p&gt;
&lt;p&gt;This is all fine so long as they&#x27;re valid, but what if you&#x27;re generating Java code without due care and attention?&lt;&#x2F;p&gt;
&lt;p&gt;And what if you&#x27;re inserting file paths into your generated code?&lt;&#x2F;p&gt;
&lt;p&gt;And what if one of your directories has a name starting with a &quot;u&quot;?&lt;&#x2F;p&gt;
&lt;p&gt;Then you get code like this (edited example from our real project!):&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; DO NOT EDIT!  This file was generated from:
&#x2F;&#x2F; C:\usr\foo.xml
&lt;&#x2F;pre&gt;
&lt;p&gt;And, only on the machine with the dir called &quot;usr&quot;, we got a compile error like this:&lt;&#x2F;p&gt;
&lt;pre&gt;MyClass.java:14: illegal unicode escape
&#x2F;&#x2F; C:\usr\foo.xml
       ^
1 error
&lt;&#x2F;pre&gt;
&lt;p&gt;Which took me a while to track down.&lt;&#x2F;p&gt;
&lt;p&gt;Reference: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;specs&#x2F;jls&#x2F;se7&#x2F;html&#x2F;jls-3.html#jls-3.3&quot;&gt;JLS-3.3 Unicode Escapes&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Note this only applies to unicode escapes, not others like \n or \t - they are only processed within character or string literals (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;specs&#x2F;jls&#x2F;se7&#x2F;html&#x2F;jls-3.html#jls-3.10.6&quot;&gt;JLS-3.10.6&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What is REST?</title>
          <pubDate>Tue, 01 Oct 2013 07:08:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/10/01/what-is-rest/</link>
          <guid>https://artificialworlds.net/blog/2013/10/01/what-is-rest/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/10/01/what-is-rest/">&lt;p&gt;Series: What is REST?, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;11&#x2F;06&#x2F;a-basic-rest-api&#x2F;&quot;&gt;A Basic REST API&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;12&#x2F;05&#x2F;paging-in-a-rest-api&#x2F;&quot;&gt;Paging&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2014&#x2F;01&#x2F;31&#x2F;searching-in-a-rest-api&#x2F;&quot;&gt;Search&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;03&#x2F;04&#x2F;security-in-a-rest-api&#x2F;&quot;&gt;Security&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;04&#x2F;29&#x2F;token-based-security-in-a-rest-api&#x2F;&quot;&gt;Token&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2014&#x2F;06&#x2F;23&#x2F;openid-security-in-a-rest-api&#x2F;&quot;&gt;OpenID&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The basic concepts of REST APIs:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;What is REST?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4aff7dce-339d-4559-bc0f-7fe0ae423e48&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;what-is-rest&#x2F;what-is-rest.html&quot;&gt;What is REST?&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Code: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;poemtube&quot;&gt;poemtube on codeberg&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Bash arrays</title>
          <pubDate>Wed, 18 Sep 2013 13:45:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/09/18/bash-arrays/</link>
          <guid>https://artificialworlds.net/blog/2013/09/18/bash-arrays/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/09/18/bash-arrays/">&lt;p&gt;Bash arrays are a lot like &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;17&#x2F;bash-associative-array-examples&#x2F;&quot;&gt;Bash Associative Arrays&lt;&#x2F;a&gt;, but with numbers as keys.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a quick reference.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;basics&quot;&gt;Basics&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR  # Create an array
$ MYARR[3]=foo      # Put a value into an array
$ echo ${MYARR[3]}  # Get a value out of an array
foo
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo MYARR[3]&lt;&#x2F;span&gt;     # WRONG
MYARR[0]
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo $MYARR[3]]&lt;&#x2F;span&gt;   # WRONG
[3]
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;creating-adding&quot;&gt;Creating, adding&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR    # Explicitly declare
$ MYARR[3]=foo        # Or this line implicitly makes it an array
$ MYARR[4]=bar        # Can add values one by one
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -a MYARR=(a b c)   # Initialise all at once
&lt;p&gt;$ echo ${MYARR[0]}
a
$ echo ${MYARR[1]}
b
$ echo ${MYARR[2]}
c
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -a MYARR   # Or declare separately
$ MYARR=(a b c)      # Then initialise
$ echo ${MYARR[0]}
a
$ echo ${MYARR[1]}
b
$ echo ${MYARR[2]}
c
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -a MYARR=(a b c)
$ MYARR=(&quot;${MYARR[@]}&quot; d)  # Add an element
$ echo ${MYARR[@]}
a b c d
$ declare -a MYARR2=(e f g)
$ MYARR=(&quot;${MYARR[@]}&quot; &quot;${MYARR2[@]}&quot;)  # Concatenate arrays
$ echo ${MYARR[@]}
a b c d e f g
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;keys-indices&quot;&gt;Keys&#x2F;Indices&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR
$ MYARR[3]=foo
$ echo ${MYARR[0]}  # Unassigned values are empty

$ echo ${MYARR[4]}  # Unassigned values are empty

$ MYARR[seven]=bar     # A text index is treated as 0
$ echo ${MYARR[0]}
bar
$ echo ${MYARR[seven]} # A text index is treated as 0
bar
&lt;&#x2F;pre&gt;&lt;pre&gt;$ K=3
&lt;p&gt;$ MYARR[$K]=baz      # Variables containing numbers work like numbers
$ echo ${MYARR[$K]}
baz
$ echo ${MYARR[3]}   # Obviously the value is accessible via the actual index
baz
$ K=foo
$ MYARR[$K]=bash     # Variables containing text are treated as 0
$ echo ${MYARR[0]}
bash
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;length&quot;&gt;Length&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR=(a b c)
$ echo ${#MYARR[@]}  # Length of an array
3
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo $#MYARR[@]&lt;&#x2F;span&gt;  # WRONG
0MYARR[@]
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo ${#MYARR}&lt;&#x2F;span&gt;   # WRONG
1
&lt;&#x2F;pre&gt;&lt;pre&gt;$ MYARR[7]=x
&lt;p&gt;$ echo ${#MYARR[@]}  # Only existing indices count in the length
4
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -a MYARR=(a bb ccc)
$ echo ${#MYARR[0]}   # Length of an individual element
1
$ echo ${#MYARR[1]}
2
$ echo ${#MYARR[2]}
3
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;looping&quot;&gt;Looping&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR=(&quot;a 1&quot; b c)
$ # Loop through array values
$ for V in &quot;${MYARR[@]}&quot;; do echo $V; done
a 1
b
c
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;for V in ${MYARR[@]}; do echo $V; done&lt;&#x2F;span&gt;  #WRONG
a
1
b
c
&lt;&#x2F;pre&gt;&lt;pre&gt;$ echo &quot;${!MYARR[@]}&quot;  # Print all indices - quoted, but quotes removed by echo
&lt;p&gt;0 1 2
$ echo &quot;${MYARR[@]}&quot;   # Print all values - quoted, but quotes removed by echo
a 1 b c
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clearing&quot;&gt;Clearing&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR
$ MYARR[3]=x

$ echo ${MYARR[3]}
x
$ unset MYARR
$ declare -a MYARR
$ echo ${MYARR[3]}

&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;deleting&quot;&gt;Deleting&lt;&#x2F;h3&gt;
&lt;pre&gt;$ MYARR[2]=foo
$ echo ${MYARR[2]}
foo
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;unset ${MYARR[2]}&lt;&#x2F;span&gt; # WRONG
$ echo ${MYARR[2]}
foo
$ unset MYARR[2]    # To delete from an array, use &quot;unset&quot; with similar syntax to assigning
$ echo ${MYARR[2]}

$ MYARR[3]=quux
$ echo ${MYARR[3]}
quux
$ K=3
$ unset MYARR[$K]   # Can unset using a variable for the key too
$ echo ${MYARR[3]}

&lt;&#x2F;pre&gt;
&lt;pre&gt;$ declare -a MYARR=(a b c d e f)
$ MYARR=(&quot;${MYARR[@]:0:3}&quot; &quot;${MYARR[@]:4}&quot;)  # Remove element 3, leaving no gap
$ echo ${MYARR[@]}
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;cool-stuff&quot;&gt;Cool stuff&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -a MYARR=(a b c d e f g)
$ echo ${MYARR[@]:2:3}              # Extract a sub-array
c d e
&lt;&#x2F;pre&gt;
&lt;pre&gt;$ declare -a MYARR=(a b c d e f g)
$ echo ${MYARR[@]&#x2F;d&#x2F;FOO}            # Replace elements that match
a b c FOO e f g
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;scope&quot;&gt;Scope&lt;&#x2F;h3&gt;
&lt;pre&gt;$ unset MYARR
$ function createmap() { MYARR[5]=bar; }  # Implicit creation puts it in the global scope
$ echo ${MYARR[5]}

$ createmap
$ echo ${MYARR[5]}
bar
&lt;&#x2F;pre&gt;&lt;pre&gt;$ unset MYARR
&lt;p&gt;$ function createmaplocal() { declare -a MYARR; MYARR[3]=bar; }  # Explicit creation puts it in the local scope
$ echo ${MYARR[3]}&lt;&#x2F;p&gt;
&lt;p&gt;$ createmaplocal
$ echo ${MYARR[3]}&lt;&#x2F;p&gt;
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;bash&#x2F;manual&#x2F;html_node&#x2F;Arrays.html&quot;&gt;Bash manual: Arrays&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.thegeekstuff.com&#x2F;2010&#x2F;06&#x2F;bash-array-tutorial&quot;&gt;Bash Array Tutorial&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;wiki.bash-hackers.org&#x2F;syntax&#x2F;arrays&quot;&gt;Bash Hackers Wiki&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.linuxjournal.com&#x2F;content&#x2F;bash-arrays&quot;&gt;Linux Journal&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;195598&#x2F;test-if-element-is-in-array-in-bash&quot;&gt;Superuser: Test if element is in array in Bash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Marcos FRM
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;On Keys&#x2F;Indices’ second box there is a typo:&lt;&#x2F;p&gt;
&lt;p&gt;$ K=3&lt;br&#x2F;&gt;
$ MYARR[$K]=baz      # Variables containing numbers work like numbers&lt;br&#x2F;&gt;
$ echo ${MYARR[$K]}&lt;br&#x2F;&gt;
bar &amp;lt;——————– THIS&lt;&#x2F;p&gt;
&lt;p&gt;It will print &amp;#39;baz&amp;#39; there.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Marcos, thank you – fixed.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bob
    &lt;span class=&quot;comment_date&quot;&gt;2013-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Note well that these should never be used in portable scripts.  &#x2F;bin&#x2F;sh may not be bash and bash may not be installed at all.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-09-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bob, yes this is specific to bash.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Using if:set, unless:set etc. with macrodefs in Ant</title>
          <pubDate>Fri, 13 Sep 2013 15:53:41 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-with-macrodefs-in-ant/</link>
          <guid>https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-with-macrodefs-in-ant/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-with-macrodefs-in-ant/">&lt;p&gt;In a previous post I outlined &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;09&#x2F;13&#x2F;using-ifset-unlessset-etc-to-execute-a-task-conditionally-in-ant&#x2F;&quot;&gt;how to use if:set and unless:set&lt;&#x2F;a&gt; to execute tasks conditionally in Ant 1.9.1.&lt;&#x2F;p&gt;
&lt;p&gt;Unfortunately, this does not work with macrodefs. When I try to execute a macrodef conditionally like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;project
    xmlns:if=&quot;ant:if&quot;
    xmlns:unless=&quot;ant:unless&quot;
    default=&quot;build&quot;
&amp;gt;

    &amp;lt;macrodef name=&quot;mymacro&quot;&amp;gt;
        &amp;lt;sequential&amp;gt;
            &amp;lt;echo message=&quot;inside mymacro&quot;&#x2F;&amp;gt;
        &amp;lt;&#x2F;sequential&amp;gt;
    &amp;lt;&#x2F;macrodef&amp;gt;

    &amp;lt;target name=&quot;build&quot;&amp;gt;
        &amp;lt;mymacro if:set=&quot;doit&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;target&amp;gt;

&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;When I set the &quot;doit&quot; property and run like this, it fails:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ant -Ddoit=true
Buildfile: build.xml

build:

BUILD FAILED
build.xml:14: Unknown attribute [ant:if:if:set]

Total time: 0 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;It looks to me like this is a bug: the if:set attribute is getting passed into the macro, which is complaining that it doesn&#x27;t expect an attribute with that name. (If you try to create an attribute with that name, you&#x27;ll find that &quot;if:set&quot; is an illegal name...)&lt;&#x2F;p&gt;
&lt;p&gt;However, there is a workaround. You can wrap the call to your macrodef in a &amp;lt;sequential&amp;gt; tag:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;project
    xmlns:if=&quot;ant:if&quot;
    xmlns:unless=&quot;ant:unless&quot;
    default=&quot;build&quot;
&amp;gt;

    &amp;lt;macrodef name=&quot;mymacro&quot;&amp;gt;
        &amp;lt;sequential&amp;gt;
            &amp;lt;echo message=&quot;inside mymacro&quot;&#x2F;&amp;gt;
        &amp;lt;&#x2F;sequential&amp;gt;
    &amp;lt;&#x2F;macrodef&amp;gt;

    &amp;lt;target name=&quot;build&quot;&amp;gt;
        &amp;lt;sequential if:set=&quot;doit&quot;&amp;gt;
            &amp;lt;mymacro&#x2F;&amp;gt;
        &amp;lt;&#x2F;sequential&amp;gt;
    &amp;lt;&#x2F;target&amp;gt;

&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;And now it works!&lt;&#x2F;p&gt;
&lt;pre&gt;$ ant -Ddoit=true
Buildfile: build.xml

build:
     [echo] inside mymacro

BUILD SUCCESSFUL
Total time: 0 seconds
&lt;&#x2F;pre&gt;&lt;pre&gt;$ ant
&lt;p&gt;Buildfile: build.xml&lt;&#x2F;p&gt;
&lt;p&gt;build:&lt;&#x2F;p&gt;
&lt;p&gt;BUILD SUCCESSFUL
Total time: 0 seconds
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Conditional execution even when calling a macrodef. Enjoy.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using if:set, unless:set etc. to execute a task conditionally in Ant</title>
          <pubDate>Fri, 13 Sep 2013 15:41:31 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-to-execute-a-task-conditionally-in-ant/</link>
          <guid>https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-to-execute-a-task-conditionally-in-ant/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/09/13/using-ifset-unlessset-etc-to-execute-a-task-conditionally-in-ant/">&lt;p&gt;I&#x27;m not sure anyone except me is still struggling on with using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ant.apache.org&#x2F;manual&#x2F;index.html&quot;&gt;Ant&lt;&#x2F;a&gt;, but just in case, here is a nice thing.&lt;&#x2F;p&gt;
&lt;p&gt;In Ant 1.9.1 they added a useful feature: instead of needing to use the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ant-contrib.sourceforge.net&#x2F;tasks&#x2F;tasks&#x2F;if.html&quot;&gt;&amp;lt;if&amp;gt;&lt;&#x2F;a&gt; tag and similar from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ant-contrib.sourceforge.net&#x2F;&quot;&gt;ant-contrib&lt;&#x2F;a&gt;, you can conditionally execute &quot;any&quot; task.&lt;&#x2F;p&gt;
&lt;p&gt;(In fact, this appears to mean &quot;any task except a macrodef&quot; - see my post &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;09&#x2F;13&#x2F;using-ifset-unlessset-etc-with-macrodefs-in-ant&#x2F;&quot;&gt;Using if:set, unless:set etc. with macrodefs in Ant&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;You need to add these namespaces at the top of your project:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;project
     xmlns:if=&quot;ant:if&quot;
     xmlns:unless=&quot;ant:unless&quot;
&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;and then make any task conditional by adding one of these attributes:&lt;pre&gt;if:set
if:true
if:blank
unless:set
unless:true
unless:blank
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;if:set and unless:set take a &lt;strong&gt;property name&lt;&#x2F;strong&gt; and run the task if it is set at all (or unless it is set at all).&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;mytask if:set=&quot;property.name&quot; other_attrs=&quot;&quot; ...&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Where mytask is the name of any ordinary Ant task like echo, jar etc.&lt;&#x2F;p&gt;
&lt;p&gt;(Note there is no ${} around the property name for if:set.)&lt;&#x2F;p&gt;
&lt;p&gt;if:true and unless:true take a &lt;strong&gt;value&lt;&#x2F;strong&gt; and run the task if it is true (or unless it is true), so they need to be used like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;mytask if:true=&quot;${property}&quot; other_attrs=&quot;&quot; ...&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;(Note the ${} here, but not above.)&lt;&#x2F;p&gt;
&lt;p&gt;Similarly, if:blank and unless:blank take a &lt;strong&gt;value&lt;&#x2F;strong&gt; and run the task if it is blank (or unless it is blank), e.g.:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;mytask if:blank=&quot;${property}&quot; other_attrs=&quot;&quot; ...&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;NOTE: The example in the documentation is wrong (at this time, 2013-09-13) - it uses the property name, but this does not work - you must surround it with ${} to get its value.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The properties can be specified in your build file as normal, or supplied on the command line to ant with -Dproperty.name=value.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of using set:&lt;&#x2F;p&gt;
&lt;p&gt;build.xml:&lt;pre&gt;&amp;lt;project
xmlns:if=&quot;ant:if&quot;
xmlns:unless=&quot;ant:unless&quot;
default=&quot;build&quot;
&amp;gt;&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;lt;property name=&amp;quot;setinxml&amp;quot; value=&amp;quot;true&amp;quot;&#x2F;&amp;amp;gt;

&amp;amp;lt;target name=&amp;quot;build&amp;quot;&amp;amp;gt;
    &amp;amp;lt;echo if:set=&amp;quot;setinxml&amp;quot;     message=&amp;quot;if:set=setinxml&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;echo unless:set=&amp;quot;setinxml&amp;quot; message=&amp;quot;unless:set=setinxml&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;echo if:set=&amp;quot;notset&amp;quot;       message=&amp;quot;if:set=notset&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;echo unless:set=&amp;quot;notset&amp;quot;   message=&amp;quot;unless:set=notset&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;echo if:set=&amp;quot;setincmd&amp;quot;     message=&amp;quot;if:set=setincmd&amp;quot;&#x2F;&amp;amp;gt;
    &amp;amp;lt;echo unless:set=&amp;quot;setincmd&amp;quot; message=&amp;quot;unless:set=setincmd&amp;quot;&#x2F;&amp;amp;gt;
&amp;amp;lt;&#x2F;target&amp;amp;gt;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And here&#x27;s the output:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ant -version
Apache Ant(TM) version 1.9.2 compiled on July 8 2013
$ ant -Dsetincmd=true
Buildfile: build.xml

build:
     [echo] if:set=setinxml
     [echo] unless:set=notset
     [echo] if:set=setincmd

BUILD SUCCESSFUL
Total time: 0 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;The documentation for this, such as it is, is here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ant.apache.org&#x2F;manual&#x2F;ifunless.html&quot;&gt;If And Unless&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    fabricio
    &lt;span class=&quot;comment_date&quot;&gt;2016-05-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;saved my day&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dev
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the info, it proved to be very useful and simple to use!!&lt;&#x2F;p&gt;
&lt;p&gt;Dev&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-07-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Dev, glad it was useful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Priyasha
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Can i do an if on entire project some how ?  is that possible ?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Priyasha, not that I know of :–(&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Priyasha Praik
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi andy, thanks for this. I have three targets in one project in my ant script. and i just want to add an if statement (effectively) which control all the three targets. so for example if i set that variable to true, then all three targets should execute. if i set it to false, nothing should execute.Can i do something like that?&lt;&#x2F;p&gt;
&lt;p&gt;str of my file: very simple:&lt;&#x2F;p&gt;
&lt;p&gt;The above is how it looks, i just want to conditionally either execute them all or not execute any.  Is there any way out ???&lt;&#x2F;p&gt;
&lt;p&gt;I am so grateful for your response again, thanks very much&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Priyasha, inside each of your targets you could have a  block, with if:set attributes for the variable you want to use to control them.  Then they would execute, but actually do nothing.  Hopefully that will work for what you want.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Priyasha Pratik
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey, thanks a lot, could you give me a small example. I am very new to ant and having a big hard time with this. Thanks, I really appreciate your help&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Vim for Fun</title>
          <pubDate>Wed, 11 Sep 2013 06:59:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/09/11/vim-for-fun/</link>
          <guid>https://artificialworlds.net/blog/2013/09/11/vim-for-fun/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/09/11/vim-for-fun/">&lt;p&gt;I have recently got quite excited about Vim (the text editor), so here is a video showing you the basics. I use it because I enjoy learning it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Vim for Fun&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e610e45b-7069-4488-8397-4bfd313889d6&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;vim-for-fun&#x2F;vim-for-fun.html&quot;&gt;Vim for Fun&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Working with others in git</title>
          <pubDate>Wed, 24 Jul 2013 18:38:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/07/24/working-with-others-in-git/</link>
          <guid>https://artificialworlds.net/blog/2013/07/24/working-with-others-in-git/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/07/24/working-with-others-in-git/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;03&#x2F;13&#x2F;why-use-git-for-source-control&#x2F;&quot;&gt;Why git?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;04&#x2F;20&#x2F;how-to-use-git-the-basics&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;13&#x2F;branches-in-git&#x2F;&quot;&gt;Branches&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;11&#x2F;merging-in-git&#x2F;&quot;&gt;Merging&lt;&#x2F;a&gt;, Remotes&lt;&#x2F;p&gt;
&lt;p&gt;Now I&#x27;ve covered almost everything you need to understand the basics of how git works. In this video I discuss how to clone, pull from and fork remote git repositories, and how to send patches and make pull requests to a project maintainer.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Working with others in git&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;72a04d3c-a10d-4628-90dc-ab18957df736&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;working-with-others-in-git&#x2F;working-with-others-in-git.html&quot;&gt;Working with others in git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Merging in git</title>
          <pubDate>Thu, 11 Jul 2013 07:29:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/07/11/merging-in-git/</link>
          <guid>https://artificialworlds.net/blog/2013/07/11/merging-in-git/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/07/11/merging-in-git/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;03&#x2F;13&#x2F;why-use-git-for-source-control&#x2F;&quot;&gt;Why git?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;04&#x2F;20&#x2F;how-to-use-git-the-basics&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;13&#x2F;branches-in-git&#x2F;&quot;&gt;Branches&lt;&#x2F;a&gt;, Merging, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;24&#x2F;working-with-others-in-git&#x2F;&quot;&gt;Remotes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The last video covered how to make branches and simple merges.&lt;&#x2F;p&gt;
&lt;p&gt;This time we look at what to do when you want just one change from another branch (cherry-picking) and how to merge two branches when there are conflicts between them. We also look at rebasing, which is a different way to combine the work done on two branches, which hides complexity from other people at the expense of changing history.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Merging in git&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;7f48c5dc-9fcc-46ed-9e32-430c0c53264f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;merging-in-git&#x2F;merging-in-git.html&quot;&gt;Merging in git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Behaviour of Java String.split() when some answers are the empty string</title>
          <pubDate>Thu, 23 May 2013 13:51:52 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/05/23/behaviour-of-java-string-split/</link>
          <guid>https://artificialworlds.net/blog/2013/05/23/behaviour-of-java-string-split/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/05/23/behaviour-of-java-string-split/">&lt;p&gt;Can you guess the output of this program?&lt;&#x2F;p&gt;
&lt;pre&gt;class SplitTest
{
    static void split( String s )
    {
        System.out.println( s.split( &quot;;&quot; ).length );
    }

    public static void main( String[] args )
    {
        split(&quot;&quot;);
        split(&quot;;&quot;);
        split(&quot;x;&quot;);
        split(&quot;;y&quot;);
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Here it is:&lt;&#x2F;p&gt;
&lt;pre&gt;$ javac SplitTest.java &amp;amp;&amp;amp; java SplitTest
1
0
1
2
&lt;&#x2F;pre&gt;
&lt;p&gt;Wow. Docs: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;docs.oracle.com&#x2F;javase&#x2F;6&#x2F;docs&#x2F;api&#x2F;java&#x2F;lang&#x2F;String.html#split%28java.lang.String%29&quot;&gt;String.split&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Side note: It&#x27;s not easy to fit Java examples into tweets.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;class X{static void x(String s){System.out.println(s.split(&quot;;&quot;).length);}public static void main(String[]a){x(&quot;&quot;);x(&quot;;&quot;);x(&quot;x;&quot;);x(&quot;;y&quot;);}}&lt;&#x2F;p&gt;
&lt;p&gt;— Andy Balaam (@andybalaam) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&#x2F;status&#x2F;337564718552207361&quot;&gt;May 23, 2013&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</description>
      </item>
      <item>
          <title>Checking the case of a filename on Windows</title>
          <pubDate>Thu, 23 May 2013 12:30:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/05/23/checking-the-case-of-a-filename-on-windows/</link>
          <guid>https://artificialworlds.net/blog/2013/05/23/checking-the-case-of-a-filename-on-windows/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/05/23/checking-the-case-of-a-filename-on-windows/">&lt;p&gt;Windows generally uses a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Case_preservation&quot;&gt;case-insensitive but not case-preserving&lt;&#x2F;a&gt; file system.&lt;&#x2F;p&gt;
&lt;p&gt;When writing some code that is intended to be used on Linux as well as Windows, I wanted it to fail on Windows in the same cases that it would fail on Linux, and this meant detecting when the case of a filename differed from its canonical case on the file system.&lt;&#x2F;p&gt;
&lt;p&gt;I want to ask &quot;is this file name correct in terms of case?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I was working in Java, but I think this issue would be similar in other languages: it&#x27;s difficult to ask for the canonical case version of a file name when we currently have a filename with abitrary case.&lt;&#x2F;p&gt;
&lt;p&gt;The only solution I came up with was to list the contents of the parent directory and check whether my arbitrary filename is listed with the correct case in the results:&lt;&#x2F;p&gt;
&lt;pre&gt;&#x2F;&#x2F; CaseCheck.java

import java.util.Arrays;
import java.io.File;
import java.io.IOException;

class CaseCheck
{
    private static File parentFile( File f )
    {
        File ret = f.getParentFile();
        if ( ret == null )
        {
            ret = new File( &quot;.&quot; );
        }
        return ret;
    }

    private static boolean existsAndCaseCorrect( String fileName )
    {
        File f = new File( fileName );
        return Arrays.asList( parentFile( f ).list() ).contains( f.getName() );
    }

    public static void main( String[] args ) throws IOException
    {
        System.out.println( existsAndCaseCorrect( args[0] ) );
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Checking it on its own source file:&lt;pre&gt;javac CaseCheck.java &amp;amp;&amp;amp; java CaseCheck cASEcheck.java
false&lt;&#x2F;p&gt;
&lt;p&gt;javac CaseCheck.java &amp;amp;&amp;amp; java CaseCheck CaseCheck.java
true
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It seems to work.&lt;&#x2F;p&gt;
&lt;p&gt;Note that this also returns false if the file doesn&#x27;t exist, and will throw an error if the file name specifies a parent directory that doesn&#x27;t exist.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Branches in git</title>
          <pubDate>Mon, 13 May 2013 20:00:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/05/13/branches-in-git/</link>
          <guid>https://artificialworlds.net/blog/2013/05/13/branches-in-git/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/05/13/branches-in-git/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;03&#x2F;13&#x2F;why-use-git-for-source-control&#x2F;&quot;&gt;Why git?&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;04&#x2F;20&#x2F;how-to-use-git-the-basics&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, Branches, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;11&#x2F;merging-in-git&#x2F;&quot;&gt;Merging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;24&#x2F;working-with-others-in-git&#x2F;&quot;&gt;Remotes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Last time we discussed starting a project and committing changes.&lt;&#x2F;p&gt;
&lt;p&gt;Now we look at how to create branches, which are one of the main reasons for having source control. We&#x27;ll cover creating branches, switching between them, and the simplest parts of merging from one to another.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Branches in git&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3387fedf-cf0d-4640-a9d6-49b42262d40e&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;branches-in-git&#x2F;branches-in-git.html&quot;&gt;Branches in git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Passing several values through a pipe in bash</title>
          <pubDate>Fri, 10 May 2013 09:13:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/05/10/passing-several-values-through-a-pipe-in-bash/</link>
          <guid>https://artificialworlds.net/blog/2013/05/10/passing-several-values-through-a-pipe-in-bash/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/05/10/passing-several-values-through-a-pipe-in-bash/">&lt;p&gt;I have been fiddling with some git-related shell scripts, and decided to try and follow the same approach as git in their structure. This means using the Unix system where each piece of functionality is a separate script (or executable) that communicates by using command-line arguments, reading from the standard input stream, and writing output to the standard output stream.&lt;&#x2F;p&gt;
&lt;p&gt;This allows each piece of functionality to be written in any programming or scripting language. In git&#x27;s case this has allowed initial versions to be written in bash or perl and later optimised versions (sometimes written in C) to be dropped in, piece by piece. It&#x27;s an incredibily flexible way of working and can also be very efficient.&lt;&#x2F;p&gt;
&lt;p&gt;Most of my prototyping has been in bash, and I&#x27;ve found sometimes I need to write out multiple values from a script and collect them as input in another script.&lt;&#x2F;p&gt;
&lt;p&gt;Writing the output is simple:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

# outputter.bash

# Imagine A, B and C have been created by some complex process:
A=&quot;foo bar&quot;
B=&quot;  bar&quot;
C=&quot;baz   &quot;

# At the end of our script we simply write them out on separate lines in a known order
echo &quot;${A}&quot;
echo &quot;${B}&quot;
echo &quot;${C}&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;But reading them in somewhere else gave me some trouble until I learned this recipe:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

# inputter.bash

# Read in the values one per line:
IFS=$&#x27;\n&#x27; read A
IFS=$&#x27;\n&#x27; read B
IFS=$&#x27;\n&#x27; read C

# Now we can use them.
echo &quot;A=&#x27;${A}&#x27;&quot;
echo &quot;B=&#x27;${B}&#x27;&quot;
echo &quot;C=&#x27;${C}&#x27;&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;And now the values transfer succesfully, preserving whitespace:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;outputter.bash | .&#x2F;inputter.bash
A=&#x27;foo bar&#x27;
B=&#x27;  bar&#x27;
C=&#x27;baz   &#x27;
&lt;&#x2F;pre&gt;
&lt;p&gt;The recipe uses bash&#x27;s built-in read command to populate the variables, but sets the IFS variable (Internal Field Separator) to a newline, meaning all the whitespace in the line is treated as part of the value to be read. The $&#x27;\n&#x27; syntax is a literal newline.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bob
    &lt;span class=&quot;comment_date&quot;&gt;2013-05-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;p&gt;is a bug.&lt;&#x2F;p&gt;
&lt;p&gt;Use  #!&#x2F;usr&#x2F;bin&#x2F;env bash intead&lt;&#x2F;p&gt;
&lt;p&gt;However, I believe none of this is bash specific so&lt;&#x2F;p&gt;
&lt;p&gt;#!&#x2F;bin&#x2F;sh&lt;br&#x2F;&gt;
is best&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-05-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bob, citation needed?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Goodness in programming languages, part 4 - Ownership &amp; Memory</title>
          <pubDate>Wed, 01 May 2013 09:00:31 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/05/01/goodness-in-programming-languages-part-4-ownership-memory/</link>
          <guid>https://artificialworlds.net/blog/2013/05/01/goodness-in-programming-languages-part-4-ownership-memory/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/05/01/goodness-in-programming-languages-part-4-ownership-memory/">&lt;p&gt;&lt;em&gt;Posts in this series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;02&#x2F;10&#x2F;goodness-in-programming-languages-part-1-syntax-and-layout&#x2F;&quot;&gt;Syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;03&#x2F;08&#x2F;goodness-in-programming-languages-part-2-getting-your-code-running&#x2F;&quot;&gt;Deployment&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;09&#x2F;05&#x2F;goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once&#x2F;&quot;&gt;Metaprogramming&lt;&#x2F;a&gt;, Ownership&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There is often a trade-off between programming language features and how fast (and predictably) the programs run. From web sites that serve millions of visitors to programs running on small devices we need to be able to make our programs run quickly.&lt;&#x2F;p&gt;
&lt;p&gt;One trade-off that is made in many modern programming languages (including Python, Ruby, C#, Java and JVM-based languages) is that the system owns all the memory. This avoids the need for the programmer to think about how long pieces of memory need to live, but it means a lot of memory can hang around a lot longer than it really needs to. In addition, it can mean the CPU has to jump around to lots of different memory locations to find pieces of dynamically-allocated memory in different locations. Where this jumping around causes caches to be invalidated that can really slow things down.&lt;&#x2F;p&gt;
&lt;p&gt;While these garbage collection-based languages have been evolving, C++ has been developing along a different track. C++ allows the programmer to allocate and free up memory manually (as in C), but over time the community of C++ programmers has been developing a new way of thinking about memory, and developing tools in the C++ language to make it easier to work in this way.&lt;&#x2F;p&gt;
&lt;p&gt;Modern C++ code rarely or never uses &quot;delete&quot; or &quot;free&quot; to deallocate memory, but instead defines clearly which object owns each other object. When the owning object is no longer needed, everything it owns can be deleted, immediately freeing their memory. The top-level objects are owned by the current scope, so when the function or block of code we are in ends, the system knows these objects and the ones they own can be deleted. Objects that last for the whole life of the program are owned by the scope of the main function or equivalent.&lt;&#x2F;p&gt;
&lt;p&gt;One advantage of explicit ownership is that the right thing happens automatically when something unexpected happens (e.g. an exception is thrown, or we return early from a function). Because the objects are owned by a scope, as soon as we exit that scope they are automatically deleted, and no memory is &quot;leaked&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Because ownership is explicit, we can often group owned objects in memory immediately next to the objects that own them. This means we jump around to different memory locations less often, and we have to do less work to find and delete regions of memory. This makes our programs faster.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some things I like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modern C++&#x27;s clarity about who owns what.&lt;&#x2F;strong&gt; By expressing ownership explicitly we make clear our intentions, and avoid memory leaks.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Modern C++&#x27;s fast and cache-friendly memory handling.&lt;&#x2F;strong&gt; Allocating memory for several objects together reduces time spent looking for space, and means caches are more likely to be used.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In my experience, the most frequent performance problems I have had to solve have really been memory problems. Explicit ownership can reduce unnecessary memory management overhead by taking back the work from the system (the garbage collector) and allowing programmers to be explicit about who owns what.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to use git (the basics)</title>
          <pubDate>Sat, 20 Apr 2013 08:05:54 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/04/20/how-to-use-git-the-basics/</link>
          <guid>https://artificialworlds.net/blog/2013/04/20/how-to-use-git-the-basics/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/04/20/how-to-use-git-the-basics/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;03&#x2F;13&#x2F;why-use-git-for-source-control&#x2F;&quot;&gt;Why git?&lt;&#x2F;a&gt;, Basics, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;13&#x2F;branches-in-git&#x2F;&quot;&gt;Branches&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;11&#x2F;merging-in-git&#x2F;&quot;&gt;Merging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;24&#x2F;working-with-others-in-git&#x2F;&quot;&gt;Remotes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Git is a very powerful tool, but somewhat intimidating at first. I will be making some videos working through how to use it step by step.&lt;&#x2F;p&gt;
&lt;p&gt;First, we look at how to track your own code on your own computer, and then get a brief look at a killer feature: stash, which lets you pause what you were doing and come back to it later.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;How to use git (the basics)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;591d3999-d5f3-4370-8c5a-560d20d9db3f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;how-to-use-git&#x2F;how-to-use-git.html&quot;&gt;How to use git (the basics) slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Using gnome-mplayer to play DVB radio without asking whether you want to resume</title>
          <pubDate>Mon, 15 Apr 2013 19:12:31 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/04/15/using-gnome-mplayer-to-play-dvb-radio-without-asking-whether-you-want-to-resume/</link>
          <guid>https://artificialworlds.net/blog/2013/04/15/using-gnome-mplayer-to-play-dvb-radio-without-asking-whether-you-want-to-resume/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/04/15/using-gnome-mplayer-to-play-dvb-radio-without-asking-whether-you-want-to-resume/">&lt;p&gt;When I launch gnome-mplayer to play back radio over my TV card (DVB), it asks me whether I want to resume from where I left off, which doesn&#x27;t make sense for this kind of stream.&lt;&#x2F;p&gt;
&lt;p&gt;I couldn&#x27;t find a way to switch this off, but a little hacking with gnome-mplayer&#x27;s sqlite database does the trick.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my Radio 4 launch script:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

URI=&quot;dvb:&#x2F;&#x2F;BBC Radio 4&quot;

sqlite3 ~&#x2F;.config&#x2F;gnome-mplayer&#x2F;gnome-mplayer.db &quot;DELETE FROM media_entries WHERE uri=&#x27;${URI}&#x27;&quot;

gnome-mplayer &quot;${URI}&quot;
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>C++14 &quot;Terse&quot; Templates - an argument against the proposed syntax</title>
          <pubDate>Sat, 13 Apr 2013 01:22:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/04/13/c14-terse-templates-an-argument-against-the-proposed-syntax/</link>
          <guid>https://artificialworlds.net/blog/2013/04/13/c14-terse-templates-an-argument-against-the-proposed-syntax/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/04/13/c14-terse-templates-an-argument-against-the-proposed-syntax/">&lt;p&gt;Today I attended two excellent talks by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.stroustrup.com&#x2F;&quot;&gt;Bjarne Stroustrup&lt;&#x2F;a&gt; at the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;conferences&#x2F;accu_conference_2013&quot;&gt;ACCU Conference 2013&lt;&#x2F;a&gt;. The first was an inspiring explanation of the recent C++11 standard, and the second, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;conferences&#x2F;accu_conference_2013&#x2F;accu2013_sessions#c_14_early_thoughts&quot;&gt;&quot;C++14 Early thoughts&quot;&lt;&#x2F;a&gt; was an exciting description of some of the features that might go into the next standard.&lt;&#x2F;p&gt;
&lt;p&gt;One of those features, which Bjarne called &quot;Terse&quot; Templates, might be a good idea, but the syntax Bjarne proposed seems like a bad idea to me, because it leaks unwanted names into the namespace containing the function you are writing.&lt;&#x2F;p&gt;
&lt;p&gt;Allow me to explain.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;background-concepts-lite&quot;&gt;Background - Concepts Lite&lt;&#x2F;h3&gt;
&lt;p&gt;I attended another excellent talk before Bjarne&#x27;s, called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;conferences&#x2F;accu_conference_2013&#x2F;accu2013_sessions#concepts_lite-constraining_templates_with_predicates&quot;&gt;&quot;Concepts Lite-Constraining Templates with Predicates&quot;&lt;&#x2F;a&gt; by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sites.google.com&#x2F;site&#x2F;andrewnsutton&#x2F;&quot;&gt;Andrew Sutton&lt;&#x2F;a&gt;, introducing &quot;Concepts Lite&quot;, which is an attempt to salvage a manageable language feature from the very large &quot;Concepts&quot; feature that failed to make it into C++11.&lt;&#x2F;p&gt;
&lt;p&gt;My (so far very basic) understanding of Concepts Lite is that it is a way of defining conditions that state whether a template will be expanded for a given type.&lt;&#x2F;p&gt;
&lt;p&gt;So, in C++11 (and C++98), we can declare a (stupid) template function like so:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename ListOfInt&amp;gt;
int first( ListOfInt&amp;amp; list ) { return list.size() &amp;gt; 0 ? list[0] : 0; }
&lt;&#x2F;pre&gt;
&lt;p&gt;The code in this function template assumes that list has a size method, and an operator[] method. We tried to &quot;suggest&quot; this, by naming our template parameter ListOfInt, but the poor programmer may not realise exactly what we meant.&lt;&#x2F;p&gt;
&lt;p&gt;If we do the wrong thing, and try to use the first function with an int argument:&lt;&#x2F;p&gt;
&lt;pre&gt;int i = 3;
first( i );
&lt;&#x2F;pre&gt;
&lt;p&gt;It goes wrong, because ints don&#x27;t have a size method:&lt;&#x2F;p&gt;
&lt;pre&gt;In function &#x27;int first(ListOfInt&amp;amp;) [with ListOfInt = int]&#x27;:
error: subscripted value is neither array nor pointer
error: request for member &#x27;size&#x27; in &#x27;list&#x27;, which is of non-class type &#x27;int&#x27;
&lt;&#x2F;pre&gt;
&lt;p&gt;This error is not too obscure, but in complex cases the errors can be extremely long, and point to problems that appear to be unrelated to the code we are writing.&lt;&#x2F;p&gt;
&lt;p&gt;Really what we want to know is that int is not a ListOfInt.&lt;&#x2F;p&gt;
&lt;p&gt;Concepts Lite give us the ability to define what a ListOfInt means, and only expand the template for types that match that definition.&lt;&#x2F;p&gt;
&lt;p&gt;In our example we would do something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename ListOfInt&amp;gt; &lt;span style=&quot;color:blue;&quot;&gt;requires SizeAndIndex&amp;lt;ListOfInt&amp;gt;()&lt;&#x2F;span&gt;
int first( ListOfInt&amp;amp; list ) { return list.size() &amp;gt; 0 ? list[0] : 0; }
&lt;&#x2F;pre&gt;
&lt;p&gt;(There is actually a neater syntax, but we&#x27;ll do it like this for now because we need the more verbose form later.)&lt;&#x2F;p&gt;
&lt;p&gt;What this means is that this template function will only be expanded for types that satisfy the constraint.&lt;&#x2F;p&gt;
&lt;p&gt;The definition of SizeAndIndex is outside the scope of this article - it allows us to check whether types satisfy some conditions. In this case we assume it checks that the type contains the methods we use.&lt;&#x2F;p&gt;
&lt;p&gt;Now when we do the wrong thing:&lt;&#x2F;p&gt;
&lt;pre&gt;int i = 3;
first( i );
&lt;&#x2F;pre&gt;
&lt;p&gt;We get a simple error message, that properly tells us what&#x27;s wrong:&lt;&#x2F;p&gt;
&lt;pre&gt;error: no matching call to â€˜first(int list)â€™
note: candidate is â€˜first(ListOfInt&amp;amp; list)â€™
note: where ListOfInt = int
note: template constraints not satisfied
note: â€˜ListOfIntâ€™ is not a&#x2F;an â€˜SizeAndIndexâ€™ type since
note: â€˜list.size()â€™ is not valid syntax
&lt;&#x2F;pre&gt;
&lt;p&gt;(The above is fiction, but Andrew assures us he gets real errors like this with his prototype.)&lt;&#x2F;p&gt;
&lt;p&gt;So Concepts Lite gives us the optional ability to check that our template parameters are what we expected them to be, giving a decent error message, instead of waiting for something to fail much later when we compile the instantiated template.&lt;&#x2F;p&gt;
&lt;p&gt;So far so utterly cool. (And, in my ill-informed opinion, the only bit of Concepts I really wanted anyway.)&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s more information on this feature here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;isocpp.org&#x2F;blog&#x2F;2013&#x2F;02&#x2F;concepts-lite-constraining-templates-with-predicates-andrew-sutton-bjarne-s&quot;&gt;Concepts Lite: Constraining Templates with Predicates&lt;&#x2F;a&gt; and here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;concepts.axiomatics.org&#x2F;~ans&#x2F;&quot;&gt;Concepts-Lite&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;constraints-on-multiple-types&quot;&gt;Constraints on multiple types&lt;&#x2F;h3&gt;
&lt;p&gt;The Concepts Lite feature as proposed allows us to specify constraints that describe how multiple types relate to each other, by doing something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename Victim1, typename Victim2&amp;gt; requires Lakosable&amp;lt;Victim1, Victim2&amp;gt;
void lakos( Victim1 a, Victim2 b );
&lt;&#x2F;pre&gt;
&lt;p&gt;Here the Lakosable constraint can specify conditions that describe how the two types relate to each other, for example that Victim1::value_type is equal to the type of Victim2.&lt;&#x2F;p&gt;
&lt;p&gt;This is very good.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the bit I want to argue against.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;terse-templates-the-syntax-i-don-t-like&quot;&gt;&quot;Terse&quot; Templates - the syntax I don&#x27;t like&lt;&#x2F;h3&gt;
&lt;p&gt;Bjarne gave us an example of the std::merge function, which has lots of arguments, and very complex constraints on them. He showed us that these could all be nicely wrapped into a single Mergeable constraint (similar to the Lakosable constraint above) but he argued that there was still too much repetition. The repetition comes from the fact that several functions in the standard library have the exact same template parameters, with the exact same constraints on them, and that you have to mention the whole list of template parameters twice: once after the template keyword, and once in the requires condition.&lt;&#x2F;p&gt;
&lt;p&gt;This led him to look for a terser syntax.&lt;&#x2F;p&gt;
&lt;p&gt;So, he proposed a modest new construct that looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;using Lakosable{Victim1,Victim2}; &#x2F;&#x2F; (1)
&lt;&#x2F;pre&gt;
&lt;p&gt;that allows a radical departure from everything that has gone before in terms of declaring templates. After we&#x27;ve made the declaration (1), we can declare the exact function we declared above with this little line:&lt;&#x2F;p&gt;
&lt;pre&gt;void lakos( Victim1 a, Victim2 b ); &#x2F;&#x2F; (2)
&lt;&#x2F;pre&gt;
&lt;p&gt;The using declaration in (1) makes the names Victim1 and Victim2 available in the current namespace, and gives them special powers that mean functions taking parameters of type Victim1 or Victim2 are automatically function templates, even though the template keyword is nowhere to be seen.&lt;&#x2F;p&gt;
&lt;p&gt;There was some resistance in the room to this proposal. Most of it focussed on (2), and the fact that templates were being declared without it being visible because of the lack of the template keyword.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m actually ok with (2). In fact, my ficticious programming language &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;pepper&#x2F;&quot;&gt;Pepper&lt;&#x2F;a&gt; (which represents everything I think is Right in programming languages) provides a feature very much like this - all non-definite parameter types act as &quot;implicit&quot; templates in Pepper (see &quot;implicit_templates.pepper&quot; on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;pepper&#x2F;examples.html&quot;&gt;Examples&lt;&#x2F;a&gt; page).&lt;&#x2F;p&gt;
&lt;p&gt;Bjarne made a reasonable defence of (2), arguing that we often want new features to be &quot;signposted&quot; by new keywords (he cited user-defined types as an example - apparently some people wanted to require &quot;class MyClass&quot; instead of just &quot;MyClass&quot; every time we referred to a user-defined type) but later when they are familiar we want less verbose syntax. (Presumably the &quot;new&quot; feature he was talking about here is templates.)&lt;&#x2F;p&gt;
&lt;p&gt;My problem is with (1).&lt;&#x2F;p&gt;
&lt;p&gt;As my neighbour in the talk (whose name I missed, sorry) pointed out, what (1) does is dump 2 new names Victim1 and Victim2 in the namespace containing the lakos function template.&lt;&#x2F;p&gt;
&lt;p&gt;No-one wants these names.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, why are we doing any of this?&lt;&#x2F;p&gt;
&lt;p&gt;The sole purpose of the exercise is to constrain the lakos function template. Why is the result putting 2 names into the namespace?&lt;&#x2F;p&gt;
&lt;p&gt;More seriously, in the case of the standard library, these names will go into the std:: namespace, and there could easily be clashes. If the std::merge function uses the name For for one of its template parameters (a Forward_iterator), and std::copy wants to use one with the same name, but with different constraints, it will override the definition of For.&lt;&#x2F;p&gt;
&lt;p&gt;I.e. If we do this:&lt;&#x2F;p&gt;
&lt;pre&gt;namespace std {
using Mergeable{For,For2,Out};
&#x2F;&#x2F; define std::merge
}

&#x2F;&#x2F; and somewhere else:

namespace std {
using Copyable{For,Out};
&#x2F;&#x2F; define std::copy
}
&lt;&#x2F;pre&gt;
&lt;p&gt;then the (useless) value of std::For will be different depending on the order in which we import the header files.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;i-think&quot;&gt;I Think&lt;&#x2F;h3&gt;
&lt;p&gt;I think.&lt;&#x2F;p&gt;
&lt;p&gt;Please correct me if I&#x27;m wrong.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;If I&#x27;m right, this all seems bad and Wrong.&lt;&#x2F;p&gt;
&lt;p&gt;What was wrong with:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename Victim1, typename Victim2&amp;gt; requires Lakosable&amp;lt;Victim1, Victim2&amp;gt;
void lakos( Victim1 a, Victim2 b );
&lt;&#x2F;pre&gt;
&lt;p&gt;anyway?&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2013-04-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;As I said on twitter, I think the point Bjarne didn’t really get across in the talk – but he said to me after (if I understood him correctly) – was that this not just about making some library corner case a bit terser. His vision is that this would be a new, default, approach to writing any kind of templates!&lt;br&#x2F;&gt;
I still need to chew on this bit overall I like the idea of overhauling the template syntax. For me it’s always been unnecessarily verbose – even without introducing constraints (and don’t get me started on the current constraints workarounds!). After all look at generics in something like C#:&lt;&#x2F;p&gt;
&lt;p&gt;void doSomethingWith( … ) { &#x2F;* … *&#x2F; }&lt;&#x2F;p&gt;
&lt;p&gt;compared to C++&lt;&#x2F;p&gt;
&lt;p&gt;template&lt;br&#x2F;&gt;
void doSomethingWith( … ) { &#x2F;* … *&#x2F; }&lt;&#x2F;p&gt;
&lt;p&gt;Do we really need the template keyword? Do we really need all those typenames? (that was my question to Bjarne and his response was that with his terse templates proposal you would never need to write another &quot;typename&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;I’m all for eliminating boilerplate. But I share the concern where it introduces different issues.&lt;br&#x2F;&gt;
One is polluting the namespace. This is unfortunate, but not a show-stopper for me. I agree with Bjarne that this is akin to introducing other names, such as class names. You just &quot;deal with it&quot;.&lt;br&#x2F;&gt;
Having said that I prefer the suggestion from the floor (I don’t know who from) to require that the concept names be qualified by the constraint names (not sure if that’s the appropriate use of those terms).&lt;br&#x2F;&gt;
However I think even the constraint name should not pollute the namespace if it only has a single use.&lt;&#x2F;p&gt;
&lt;p&gt;My beef is more with the non-local effects. If this is the default way to write templates – not just how you might share constraints across templates – then there seems to be a disconnect between the constraints and the template functions and classes that use them.&lt;&#x2F;p&gt;
&lt;p&gt;Interesting times…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-04-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;His vision is that this would be a new, default, approach to writing any kind of templates!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sounds good to me – I just don’t like the way it’s done here.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I agree with Bjarne that this is akin to introducing other names, such as class names.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;But those class names are potentially useful, whereas these aren’t?&lt;&#x2F;p&gt;
&lt;p&gt;I agree about non-local effects.&lt;&#x2F;p&gt;
&lt;p&gt;I think Bjarne is mixing together a general idea that templates could&#x2F;should become lighter on syntax with a specific idea about how to express constraints.  I don’t see how they go together at all.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>setUp and tearDown considered harmful</title>
          <pubDate>Wed, 27 Mar 2013 15:08:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/03/27/setup-and-teardown-considered-harmful/</link>
          <guid>https://artificialworlds.net/blog/2013/03/27/setup-and-teardown-considered-harmful/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/03/27/setup-and-teardown-considered-harmful/">&lt;p&gt;Some unit test frameworks provide methods (often called setUp and tearDown, or annotated with @Before and @After) that are called automatically before a unit test executes, and afterwards.&lt;&#x2F;p&gt;
&lt;p&gt;This structure is presumably intended to avoid repetition of code that is identical in all the tests within one test file.&lt;&#x2F;p&gt;
&lt;p&gt;I have always instinctively avoided using these methods, and when a colleague used them recently I thought I should try to write up why I feel negative about them. Here it is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: note I am talking about unit tests here. I know these frameworks can be used for other types of test, and maybe in that context these methods could be useful.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-it-s-action-at-a-distance&quot;&gt;1. It&#x27;s action at a distance&lt;&#x2F;h3&gt;
&lt;p&gt;setUp and tearDown are called automatically, with no indication in your code that you use them, or don&#x27;t use them. They are &quot;magic&quot;, and everyone hates magic.&lt;&#x2F;p&gt;
&lt;p&gt;If someone is reading your test (because it broke, probably) they don&#x27;t know whether some setUp will be called without manually scanning your code to find out whether it exists. Do you hate them?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-setup-contains-useless-stuff&quot;&gt;2. setUp contains useless stuff&lt;&#x2F;h3&gt;
&lt;p&gt;How many tests do you have in one file? When you first write it, maybe, just maybe, all the tests need the exact same setup. Later, you&#x27;ll write new tests that only use part of it.&lt;&#x2F;p&gt;
&lt;p&gt;Very soon, you grow an uber-setUp that does all the setup for various different tests, creating objects you don&#x27;t need. This adds complexity for everyone who has to read your tests - they don&#x27;t know which bits of setUp are used in this test, and which are cruft for something else.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-they-require-member-variables&quot;&gt;3. They require member variables&lt;&#x2F;h3&gt;
&lt;p&gt;The only useful work you can do inside setUp and tearDown is creating and modifying member variables.&lt;&#x2F;p&gt;
&lt;p&gt;Now your tests aren&#x27;t self-contained - they use these member variables, and you must make absolutely sure that your test works no matter what state they are in. These member variables are not useful for anything else - they are purely an artifact of the choice to use setUp and tearDown.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-a-named-function-is-better&quot;&gt;4. A named function is better&lt;&#x2F;h3&gt;
&lt;p&gt;When you have setup code to share, write a function or method. Give it a name, make it return the thing it creates. By giving it a name you make your test easier to read. By returning what it creates, you avoid the use of member variables. By avoiding the magic setUp method, you give yourself the option of calling more than one setup function, making code re-use more granular (if you want).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-what-goes-in-teardown&quot;&gt;5. What goes in tearDown?&lt;&#x2F;h3&gt;
&lt;p&gt;If you&#x27;re using tearDown, what are you doing?&lt;&#x2F;p&gt;
&lt;p&gt;Are you tearing down some global state? I thought this was a unit test?&lt;&#x2F;p&gt;
&lt;p&gt;Are you ensuring nothing is left in an unpredictable state for future tests? Surely those tests guarantee their state at the start?&lt;&#x2F;p&gt;
&lt;p&gt;What possible use is there for this function?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h3&gt;
&lt;p&gt;A unit test should be a self-contained pure function, with no dependencies on other state. setUp and tearDown force you to depend on member variables of your test class, for no benefits over named functions, except that you don&#x27;t have to type their names. I consider typing the name of a properly-named function to be a benefit.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Good post. You’re very much in tune with the thinking of James Newkirk, who led the NUnit team, and later wrote x-Unit:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;jamesnewkirk.typepad.com&#x2F;posts&#x2F;2007&#x2F;09&#x2F;why-you-should-.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;jamesnewkirk.typepad.com&#x2F;posts&#x2F;2007&#x2F;09...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It’s also something that gets consideration in CATCH (my C++ test framework). In CATCH you can write a self contained function that sets up state, on the stack (as this is C++), and inline with a number a Sections that run in isolation but share that set-up code.&lt;br&#x2F;&gt;
Because you can nest Sections you can share just the setup that is common to child Sections and even have bits of setup shared between more specific bits of setup – and all in a way that is a lot simpler than I just made it sound :–)&lt;&#x2F;p&gt;
&lt;p&gt;Come to my ACCU 2013 talk to see it in action ;–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Phil, thanks!  As ever, your talk clashes with about 3 others I want to see.  I am really interested in CATCH :(&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;try catch me after (see what I did there?)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jonathan Allen
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;No, its not a &quot;unit test&quot;. Stop assuming that every test has to be a unit test.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Fair enough.  If it’s not a unit test maybe there is a use for these functions.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Phil: will do :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Why use git for source control?</title>
          <pubDate>Wed, 13 Mar 2013 08:20:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/03/13/why-use-git-for-source-control/</link>
          <guid>https://artificialworlds.net/blog/2013/03/13/why-use-git-for-source-control/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/03/13/why-use-git-for-source-control/">&lt;p&gt;Series: Why git?, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;04&#x2F;20&#x2F;how-to-use-git-the-basics&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;13&#x2F;branches-in-git&#x2F;&quot;&gt;Branches&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;11&#x2F;merging-in-git&#x2F;&quot;&gt;Merging&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;07&#x2F;24&#x2F;working-with-others-in-git&#x2F;&quot;&gt;Remotes&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Putting your code in git is fast, flexible and powerful. You can track versions on a single machine, or scale up to thousands of people working together, with sub-teams, reviews and cherry-picking of changes. Don&#x27;t fear branching any more:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Why use git for source control?&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;aceda739-d879-44b4-b997-11265f0869f9&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;why-use-git&#x2F;why-use-git.html&quot;&gt;Why use git for source control? slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Set the volume in OpenBox&#x2F;LXDE (or on the command line) with PulseAudio and Ubuntu</title>
          <pubDate>Tue, 05 Mar 2013 11:09:13 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/03/05/set-the-volume-in-openboxlxde-or-on-the-command-line-with-pulseaudio-and-ubuntu/</link>
          <guid>https://artificialworlds.net/blog/2013/03/05/set-the-volume-in-openboxlxde-or-on-the-command-line-with-pulseaudio-and-ubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/03/05/set-the-volume-in-openboxlxde-or-on-the-command-line-with-pulseaudio-and-ubuntu/">&lt;p&gt;I am switching to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lxde.org&#x2F;&quot;&gt;LXDE&lt;&#x2F;a&gt;, and enjoying it, but a few things require some manual config before it&#x27;s just how I like it.&lt;&#x2F;p&gt;
&lt;p&gt;To control the sound volume with the volume buttons, the default LXDE config in ~&#x2F;openbox&#x2F;lxde-rc.xml contains an entry like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;!-- Doesn&#x27;t work for me --&amp;gt;
&amp;lt;keybind key=&quot;XF86AudioRaiseVolume&quot;&amp;gt;
  &amp;lt;action name=&quot;Execute&quot;&amp;gt;
    &amp;lt;command&amp;gt;amixer -q sset Master 3%+&amp;lt;&#x2F;command&amp;gt;
  &amp;lt;&#x2F;action&amp;gt;
&amp;lt;&#x2F;keybind&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;(Inside the &amp;lt;keyboard&amp;gt; section.)&lt;&#x2F;p&gt;
&lt;p&gt;This doesn&#x27;t work for me, but we can do it by sending a command to PulseAudio, using the pactl command. The command to increase the volume is:&lt;&#x2F;p&gt;
&lt;pre&gt;pactl -- set-sink-volume 0 +5%
&lt;&#x2F;pre&gt;
&lt;p&gt;To decrease the volume, put &quot;-5%&quot; instead of &quot;+5%&quot;. Note that if you have more than one enabled audio sink you might need to change the &quot;0&quot; to a &quot;1&quot; or something else. Running pactl stat should help you here.&lt;&#x2F;p&gt;
&lt;p&gt;[Beware of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;osdir.com&#x2F;ml&#x2F;general&#x2F;2012-09&#x2F;msg04416.html&quot;&gt;bug 686667&lt;&#x2F;a&gt;, meaning you can&#x27;t use pacmd and you must have the -- at the beginning.]&lt;&#x2F;p&gt;
&lt;p&gt;So the correct recipe for your OpenBox config in ~&#x2F;openbox&#x2F;lxde-rc.xml is:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;keybind key=&quot;XF86AudioRaiseVolume&quot;&amp;gt;
  &amp;lt;action name=&quot;Execute&quot;&amp;gt;
    &amp;lt;command&amp;gt;pactl -- set-sink-volume 0 +5%&amp;lt;&#x2F;command&amp;gt;
  &amp;lt;&#x2F;action&amp;gt;
&amp;lt;&#x2F;keybind&amp;gt;
&amp;lt;keybind key=&quot;XF86AudioLowerVolume&quot;&amp;gt;
  &amp;lt;action name=&quot;Execute&quot;&amp;gt;
    &amp;lt;command&amp;gt;pactl -- set-sink-volume 0 -5%&amp;lt;&#x2F;command&amp;gt;
  &amp;lt;&#x2F;action&amp;gt;
&amp;lt;&#x2F;keybind&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;After editing this file you can run:&lt;&#x2F;p&gt;
&lt;pre&gt;openbox --reconfigure
&lt;&#x2F;pre&gt;
&lt;p&gt;To update without restarting OpenBox.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, because I don&#x27;t have volume control buttons, I want my volume to change with Ctrl-Alt-PageUp and Ctrl-Alt-PageDown, so I use this recipe:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;keybind key=&quot;C-A-Prior&quot;&amp;gt;
  &amp;lt;action name=&quot;Execute&quot;&amp;gt;
    &amp;lt;command&amp;gt;pactl -- set-sink-volume 0 +5%&amp;lt;&#x2F;command&amp;gt;
  &amp;lt;&#x2F;action&amp;gt;
&amp;lt;&#x2F;keybind&amp;gt;
&amp;lt;keybind key=&quot;C-A-Next&quot;&amp;gt;
  &amp;lt;action name=&quot;Execute&quot;&amp;gt;
    &amp;lt;command&amp;gt;pactl -- set-sink-volume 0 -5%&amp;lt;&#x2F;command&amp;gt;
  &amp;lt;&#x2F;action&amp;gt;
&amp;lt;&#x2F;keybind&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;but that&#x27;s just me.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dakota
    &lt;span class=&quot;comment_date&quot;&gt;2015-05-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;much appreciated! I’m using an old laptop so I switched from Cinnamon (which I prefer, but it’s bulky) to LXDE. I had the same problem. reconfiguring Openbox gives me flashbacks to when I used Crunchbang, as the media buttons didn’t work by  default.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Everybody loves build.xml (test-driven Ant)</title>
          <pubDate>Sat, 16 Feb 2013 14:39:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/02/16/everybody-loves-build-xml-test-driven-ant/</link>
          <guid>https://artificialworlds.net/blog/2013/02/16/everybody-loves-build-xml-test-driven-ant/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/02/16/everybody-loves-build-xml-test-driven-ant/">&lt;p&gt;In the &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;02&#x2F;08&#x2F;everybody-hates-build-xml-code-reuse-in-ant&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt; we looked at how it is possible to write reasonable code in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ant.apache.org&#x2F;&quot;&gt;Ant&lt;&#x2F;a&gt;, by writing small re-usable blocks of code.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, if you&#x27;re going to have any confidence in your build file you&#x27;re going to need to test it. Now we&#x27;ve learnt some basic Ant techniques, we&#x27;re ready to do the necessary magic that allows us to write tests.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Everybody loves build.xml (test-driven Ant)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;207b23a0-eed3-4f6f-8bd0-e88eee18abb8&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;ant-tdd&#x2F;ant-tdd.html&quot;&gt;Everybody loves build.xml slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First, let me clear up &lt;em&gt;what&lt;&#x2F;em&gt; we&#x27;re testing:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-do-we-want-to-test&quot;&gt;What do we want to test?&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;re not testing our Java code. We know how to do that, and to run tests, if we&#x27;ve written them using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.junit.org&#x2F;&quot;&gt;JUnit&lt;&#x2F;a&gt;, just needs a &amp;lt;junit&amp;gt; tag in our build.xml. (Other testing frameworks are available and some people say they&#x27;re better).&lt;&#x2F;p&gt;
&lt;p&gt;The things we want to test are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;build artifacts&lt;&#x2F;strong&gt; - the &quot;output&quot; of our builds i.e. JAR files, zips and things created when we run the build,&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;build logic&lt;&#x2F;strong&gt; - such as whether dependencies are correct, whether the build succeeds or fails under certain conditions, and&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;units of code&lt;&#x2F;strong&gt; - checking whether individual macros or code snippets are correct.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Note, if you&#x27;re familiar with the terminology, that testing build artifacts can never be a &quot;unit test&quot;, since it involves creating real files on the disk and running the real build.&lt;&#x2F;p&gt;
&lt;p&gt;Below we&#x27;ll see how I found ways to test build artifacts, and some ideas I had to do the other two, but certainly not a comprehensive solution. Your contributions are welcome.&lt;&#x2F;p&gt;
&lt;p&gt;Before we start, let&#x27;s see how I&#x27;m laying out my code:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;code-layout&quot;&gt;Code layout&lt;&#x2F;h2&gt;
&lt;pre&gt;build.xml      - real build file
asserts.xml    - support code for tests
test-build.xml - actual tests
&lt;&#x2F;pre&gt;
&lt;p&gt;I have a normal build file called build.xml, a file containing support code for the tests (mostly macros allowing us to make assertions) called asserts.xml, and a file containing the actual tests called test-build.xml.&lt;&#x2F;p&gt;
&lt;p&gt;To run the tests I invoke Ant like this:&lt;&#x2F;p&gt;
&lt;pre&gt;ant -f test-build.xml test-name
&lt;&#x2F;pre&gt;
&lt;p&gt;test-build.xml uses an include to get the assertions:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;include file=&quot;asserts.xml&quot;&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Tests call a target inside build.xml using subant, then use the code in asserts.xml to make assertions about what happened.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simple-example-code-got-compiled&quot;&gt;Simple example: code got compiled&lt;&#x2F;h2&gt;
&lt;p&gt;If we want to check that a &amp;lt;javac ...&amp;gt; task worked, we can just check that a .class file was created:&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the test, in test-build.xml:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;test-class-file-created&quot;&amp;gt;
    &amp;lt;assert-target-creates-file
        target=&quot;build&quot;
        file=&quot;bin&#x2F;my&#x2F;package&#x2F;ExampleFile.class&quot;
    &#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;We run it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;ant -f test-build.xml test-class-file-created&lt;&#x2F;pre&gt;
&lt;p&gt;The assert-target-creates-file assertion is a macrodef in asserts.xml like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;assert-target-creates-file&quot;&amp;gt;
    &amp;lt;attribute name=&quot;target&quot;&#x2F;&amp;gt;
    &amp;lt;attribute name=&quot;file&quot;&#x2F;&amp;gt;
    &amp;lt;sequential&amp;gt;
        &amp;lt;delete file=&quot;@{file}&quot; quiet=&quot;true&quot;&#x2F;&amp;gt;
        &amp;lt;subant antfile=&quot;build.xml&quot; buildpath=&quot;.&quot; target=&quot;@{target}&quot;&#x2F;&amp;gt;
        &amp;lt;assert-file-exists file=&quot;@{file}&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;It just deletes a file, runs the target using subant, then asserts that the file exists, which uses this macrodef:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;assert-file-exists&quot;&amp;gt;
    &amp;lt;attribute name=&quot;file&quot;&#x2F;&amp;gt;
    &amp;lt;sequential&amp;gt;
        &amp;lt;echo message=&quot;Checking existence of file: @{file}&quot;&#x2F;&amp;gt;
        &amp;lt;fail message=&quot;File &#x27;@{file}&#x27; does not exist.&quot;&amp;gt;
            &amp;lt;condition&amp;gt;
                &amp;lt;not&amp;gt;&amp;lt;available file=&quot;@{file}&quot;&#x2F;&amp;gt;&amp;lt;&#x2F;not&amp;gt;
            &amp;lt;&#x2F;condition&amp;gt;
        &amp;lt;&#x2F;fail&amp;gt;
    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This uses a trick I&#x27;ve used a lot, which is the fail task, with a condition inside it, meaning that we only fail if the condition is satisfied. Here we use not available which means fail if the file doesn&#x27;t exist.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;harder-example-jar-file&quot;&gt;Harder example: JAR file&lt;&#x2F;h2&gt;
&lt;p&gt;Now let&#x27;s check that a JAR file was created, and has the right contents. Here&#x27;s the test:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;test-jar-created-with-manifest&quot;&amp;gt;

    &amp;lt;assert-target-creates-file
        target=&quot;build&quot;
        file=&quot;dist&#x2F;MyProduct.jar&quot;
    &#x2F;&amp;gt;
    &amp;lt;assert-file-in-jar-contains
        jarfile=&quot;dist&#x2F;MyProduct.jar&quot;
        filename=&quot;MANIFEST.MF&quot;
        find=&quot;Main-Class: my.package.MyMain&quot;
    &#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This just says after we&#x27;re run the target, the file MyProduct.jar exists, and it contains a file called MANIFEST.MF that has the right Main-Class information in it.&lt;&#x2F;p&gt;
&lt;p&gt;assert-file-in-jar-contains looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;assert-file-in-jar-contains&quot;&amp;gt;
    &amp;lt;attribute name=&quot;jarfile&quot;&#x2F;&amp;gt;
    &amp;lt;attribute name=&quot;filename&quot;&#x2F;&amp;gt;
    &amp;lt;attribute name=&quot;find&quot;&#x2F;&amp;gt;

    &amp;lt;sequential&amp;gt;
        &amp;lt;!-- ... insert checks that jar exists, and contains file --&amp;gt;

        &amp;lt;delete dir=&quot;${tmpdir}&#x2F;unzip&quot;&#x2F;&amp;gt;
        &amp;lt;unzip src=&quot;@{jarfile}&quot; dest=&quot;${tmpdir}&#x2F;unzip&quot;&#x2F;&amp;gt;

        &amp;lt;fail message=&quot;@{jarfile}:@{filename} should contain @{find}&quot;&amp;gt;
            &amp;lt;condition&amp;gt;
                &amp;lt;resourcecount when=&quot;equal&quot; count=&quot;0&quot;&amp;gt;
                    &amp;lt;fileset dir=&quot;${tmpdir}&#x2F;unzip&quot;&amp;gt;
                        &amp;lt;and&amp;gt;
                            &amp;lt;filename name=&quot;**&#x2F;@{filename}&quot;&#x2F;&amp;gt;
                            &amp;lt;contains text=&quot;@{find}&quot;&#x2F;&amp;gt;
                        &amp;lt;&#x2F;and&amp;gt;
                    &amp;lt;&#x2F;fileset&amp;gt;
                &amp;lt;&#x2F;resourcecount&amp;gt;
            &amp;lt;&#x2F;condition&amp;gt;
        &amp;lt;&#x2F;fail&amp;gt;

        &amp;lt;delete dir=&quot;${tmpdir}&#x2F;unzip&quot;&#x2F;&amp;gt;

    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Which basically unzips the JAR into a directory, then searches the directory using fileset for a file with the right name and contents, and fails if it&#x27;s not found (i.e. if the resourcecount of the fileset is zero. These are the kinds of backflips you need to do to bend Ant to your will.&lt;&#x2F;p&gt;
&lt;p&gt;Or, you can choose the Nuclear Option.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-nuclear-option&quot;&gt;The Nuclear Option&lt;&#x2F;h2&gt;
&lt;p&gt;If ant tasks just won&#x27;t do, since Ant 1.7 and Java 1.6 we can drop into a &amp;lt;script&amp;gt; tag.&lt;&#x2F;p&gt;
&lt;p&gt;You ain&#x27;t gonna like it:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;script language=&quot;javascript&quot;&amp;gt;&amp;lt;![CDATA[
system.launchMissiles(); &#x2F;&#x2F; Muhahahaha
]]&amp;gt;&amp;lt;&#x2F;script&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;The script tag allows us to use a scripting language as provided through the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.drdobbs.com&#x2F;jvm&#x2F;jsr-223-scripting-for-the-java-platform&#x2F;215801163&quot;&gt;JSR 223&lt;&#x2F;a&gt; Java feature directly within our Ant file, meaning we can do anything.&lt;&#x2F;p&gt;
&lt;p&gt;In all the JVMs I&#x27;ve tried, the only scripting language actually available is JavaScript, provided by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Rhino&quot;&gt;Rhino&lt;&#x2F;a&gt; virtual machine, which is now part of standard Java.&lt;&#x2F;p&gt;
&lt;p&gt;When using the script tag, expect bad error messages. Rhino produces unhelpful stack traces, and Ant doesn&#x27;t really tell you what went wrong.&lt;&#x2F;p&gt;
&lt;p&gt;So now we know how to test the artifacts our build produces, but what about directly testing the logic in build.xml?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-build-logic&quot;&gt;Testing build logic&lt;&#x2F;h2&gt;
&lt;p&gt;We want to:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Confirm that targets succeed or fail under certain conditions&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Check indirect dependencies are as expected&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Test a unit of Ant logic (e.g. a macrodef)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;success-and-failure&quot;&gt;Success and failure&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s a little macro I cooked up to assert that something is going to fail:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;expect-failure&quot;&amp;gt;
    &amp;lt;attribute name=&quot;target&quot;&#x2F;&amp;gt;
    &amp;lt;sequential&amp;gt;
        &amp;lt;local name=&quot;ex.caught&quot;&#x2F;&amp;gt;
        &amp;lt;script language=&quot;javascript&quot;&amp;gt;&amp;lt;![CDATA[
        try {
            project.executeTarget( &quot;@{target}&quot; );
        } catch( e ) {
            project.setProperty( &quot;ex.caught&quot;, &quot;yes&quot; )
        }
        ]]&amp;gt;&amp;lt;&#x2F;script&amp;gt;
        &amp;lt;fail message=&quot;@{target} succeeded!!!&quot; unless=&quot;ex.caught&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;I resorted to the Nuclear Option of a script tag, and used Ant&#x27;s Java API (through JavaScript) to execute the target, and catch any exceptions that are thrown. If no exception is thrown, we fail.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-dependencies&quot;&gt;Testing dependencies&lt;&#x2F;h3&gt;
&lt;p&gt;To check that the dependencies are as we expect, we really want to run ant&#x27;s dependency resolution without doing anything. Remarkably, ant has no support for this.&lt;&#x2F;p&gt;
&lt;p&gt;But we can hack it in:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;printCdeps&quot;&amp;gt;
    &amp;lt;script language=&quot;javascript&quot;&amp;gt;&amp;lt;![CDATA[

        var targs = project.getTargets().elements();
        while( targs.hasMoreElements() )
        {
            var targ = targs.nextElement();
            targ.setUnless( &quot;DRY.RUN&quot; );
        }
        project.setProperty( &quot;DRY.RUN&quot;, &quot;1&quot; );
        project.executeTarget( &quot;targetC&quot; );

    ]]&amp;gt;&amp;lt;&#x2F;script&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;(See &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;31&#x2F;dry-run-mode-for-ant-ant-n-ant-dry-run&#x2F;&quot;&gt;Dry run mode for Ant&lt;&#x2F;a&gt; for more.)&lt;&#x2F;p&gt;
&lt;p&gt;Now we need to be able to run a build and capture the output. We can do that like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;test-C-depends-on-A&quot;&amp;gt;
    &amp;lt;delete file=&quot;${tmpdir}&#x2F;cdeps.txt&quot;&#x2F;&amp;gt;
    &amp;lt;ant
        target=&quot;printCdeps&quot;
        output=&quot;${tmpdir}&#x2F;cdeps.txt&quot;
    &#x2F;&amp;gt;
    &amp;lt;fail message=&quot;Target A did not execute when we ran C!&quot;&amp;gt;
        &amp;lt;condition&amp;gt;
            &amp;lt;resourcecount when=&quot;equal&quot; count=&quot;0&quot;&amp;gt;
                &amp;lt;fileset file=&quot;${tmpdir}&#x2F;cdeps.txt&quot;&amp;gt;
                    &amp;lt;contains text=&quot;targetA:&quot;&#x2F;&amp;gt;
                &amp;lt;&#x2F;fileset&amp;gt;
            &amp;lt;&#x2F;resourcecount&amp;gt;
        &amp;lt;&#x2F;condition&amp;gt;
    &amp;lt;&#x2F;fail&amp;gt;
    &amp;lt;delete file=&quot;${tmpdir}&#x2F;cdeps.txt&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;We use ant to run the build, telling it to write to a file cdeps.txt. Then, to assert that C depends on A, we just fail if cdeps.txt doesn&#x27;t contain a line indicating we ran A. (To assert a file contains a certain line we use a load of fail, condition, resourcecount and fileset machinery as before.)&lt;&#x2F;p&gt;
&lt;p&gt;So, we can check that targets depend on each other, directly or indirectly. Can we write proper unit tests for our macrodefs?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;testing-ant-units&quot;&gt;Testing ant units&lt;&#x2F;h3&gt;
&lt;p&gt;To test a macrodef or target as a piece of logic, without touching the file system or really running it, we will need fake versions of all the tasks, including &amp;lt;jar&amp;gt;, &amp;lt;copy&amp;gt;, &amp;lt;javac&amp;gt; and many more.&lt;&#x2F;p&gt;
&lt;p&gt;If we replace the real versions with fakes and then run our tasks, we can set up our fakes to track what happened, and then make assertions about it.&lt;&#x2F;p&gt;
&lt;p&gt;If we create a file called real-fake-tasks.xml, we can put things like this inside:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;jar&quot;&amp;gt;
    &amp;lt;attribute name=&quot;destfile&quot;&#x2F;&amp;gt;
    &amp;lt;sequential&amp;gt;
        &amp;lt;property name=&quot;jar.was.run&quot; value=&quot;yes&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;and, in build.xml we include something called fake-tasks.xml, with the optional attribute set to true:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;include file=&quot;fake-tasks.xml&quot; optional=&quot;true&quot;&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;If the target we want to test looks like this (in build.xml):&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;targetA&quot;&amp;gt;
    &amp;lt;jar destfile=&quot;foo.jar&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Then we can write a test like this in test-build.xml:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;test-A-runs-jar&quot; depends=&quot;build.targetA&quot;&amp;gt;
    &amp;lt;fail message=&quot;Didn&#x27;t jar!&quot; unless=&quot;jar.was.run&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;and run the tests like this:&lt;&#x2F;p&gt;
&lt;pre&gt;cp real-fake-tasks.xml fake-tasks.xml
ant -f test-build.xml test-A-runs-jar
rm fake-tasks.xml
&lt;&#x2F;pre&gt;
&lt;p&gt;If fake-tasks.xml doesn&#x27;t exist, the real tasks will be used, so running your build normally should still work.&lt;&#x2F;p&gt;
&lt;p&gt;This trick relies on the fact that our fake tasks replace the real ones, which appears to be an undocumented behaviour of my version of Ant. Ant complains about us doing this, with an error message that sounds like it didn&#x27;t work, but actually it did (on my machine).&lt;&#x2F;p&gt;
&lt;p&gt;If we wanted to avoid relying on this undocumented behaviour, we&#x27;d need to write our real targets based on special macrodefs called things like do-jar and provide a version of do-jar that hands off to the real jar, and a version that is a fake. This would be a lot of work, and pollutes our production code with machinery needed for testing, but it could work with Ant&#x27;s documented behaviour, making it unlikely to fail unexpectedly in the future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h2&gt;
&lt;p&gt;You can write Ant code in a test-driven way, and there are even structures that allow you to write things that might be described as unit tests.&lt;&#x2F;p&gt;
&lt;p&gt;At the moment, I am using mostly the &quot;testing artifacts&quot; way. The tests run slowly, but they give real confidence that your build file is really working.&lt;&#x2F;p&gt;
&lt;p&gt;Since I introduced this form of testing into our build, I enjoy working with build.xml a lot more, because I know when I&#x27;ve messed it up.&lt;&#x2F;p&gt;
&lt;p&gt;But I do spend more time waiting around for the tests to run.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Per Arnold Blaasmo
    &lt;span class=&quot;comment_date&quot;&gt;2013-09-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, I have been working in a similar scope trying to do TDD on the ANT build scripts themselves.&lt;br&#x2F;&gt;
I have used antunit (&lt;a href=&quot;http:&#x2F;&#x2F;ant.apache.org&#x2F;antlibs&#x2F;antunit&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;ant.apache.org&#x2F;antlibs&#x2F;antunit&#x2F;&lt;&#x2F;a&gt;) for doing asserts on macrodefs.&lt;&#x2F;p&gt;
&lt;p&gt;Have you considered using Antunit?&lt;&#x2F;p&gt;
&lt;p&gt;Regards&lt;br&#x2F;&gt;
Per A.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-09-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Per, no, I didn’t find it when I was looking for something like this.  Thanks for pointing it out.  It looks pretty similar to what I’ve home-grown myself.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Everybody hates build.xml (code reuse in Ant)</title>
          <pubDate>Fri, 08 Feb 2013 08:03:59 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/02/08/everybody-hates-build-xml-code-reuse-in-ant/</link>
          <guid>https://artificialworlds.net/blog/2013/02/08/everybody-hates-build-xml-code-reuse-in-ant/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/02/08/everybody-hates-build-xml-code-reuse-in-ant/">&lt;p&gt;If you&#x27;re starting a new Java project, I&#x27;d suggest suggest considering the many alternatives to Ant, including &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gant.codehaus.org&#x2F;&quot;&gt;Gant&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gradle.org&#x2F;&quot;&gt;Gradle&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.scons.org&#x2F;&quot;&gt;SCons&lt;&#x2F;a&gt; and, of course, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;make&#x2F;&quot;&gt;Make&lt;&#x2F;a&gt;. This post is about how to bend Ant to work like a programming language, so you can write good code in it. It&#x27;s seriously worth considering a build tool that actually is a programming language.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;ve chosen Ant, or you&#x27;re stuck with Ant, read on.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Everybody hates build.xml (code reuse in Ant)&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;231e0583-a5fd-4c16-80b4-12207df05e12&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Slides: &lt;a href=&quot;&#x2F;presentations&#x2F;ant-code-reuse&#x2F;ant-code-reuse.html&quot;&gt;Everybody hates build.xml slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Most projects I&#x27;ve been involved with that use Ant have a hateful build.xml surrounded by fear. The most important reason for this is that the functionality of the build file is not properly tested, so you never know whether you&#x27;ve broken it, meaning you never make &quot;non-essential&quot; changes i.e. changes that make it easier to use or read. A later post and video will cover how to test your build files, but first we must address a pre-requisite:&lt;&#x2F;p&gt;
&lt;p&gt;Can you write good code in Ant, even if you aren&#x27;t paralysed by fear?&lt;&#x2F;p&gt;
&lt;p&gt;One of the most important aspects of good code is that you only need to express each concept once. Or, to put it another way, you can re-use code.&lt;&#x2F;p&gt;
&lt;p&gt;I want to share with you some of the things I have discovered recently about Ant, and how you should (and should not) re-use code.&lt;&#x2F;p&gt;
&lt;p&gt;But first:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-ant&quot;&gt;What is Ant?&lt;&#x2F;h2&gt;
&lt;p&gt;Ant is 2 languages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A declarative language to describe dependencies&lt;&#x2F;li&gt;
&lt;li&gt;A procedural language to prescribe actions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In fact, it&#x27;s just like a Makefile (ignore this if Makefiles aren&#x27;t familiar). A Makefile rule consists of a target (the name before the colon) with its dependencies (the names after the colon), which make up a declarative description of the dependencies, and the commands (the things indented by tabs) which are a normal procedural description of what to do to build that target.&lt;&#x2F;p&gt;
&lt;pre&gt;# Ignore this if you don&#x27;t care about Makefiles!
target: dep1 dep2   # Declarative
    action1         # Procedural
    action2
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;the-declarative-language&quot;&gt;The declarative language&lt;&#x2F;h3&gt;
&lt;p&gt;In Ant, the declarative language is a directed graph of targets and dependencies:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;A&quot;&#x2F;&amp;gt;
&amp;lt;target name=&quot;B&quot; depends=&quot;A&quot;&#x2F;&amp;gt;
&amp;lt;target name=&quot;C&quot; depends=&quot;B&quot;&#x2F;&amp;gt;
&amp;lt;target name=&quot;D&quot; depends=&quot;A&quot;&#x2F;&amp;gt;
&lt;&#x2F;pre&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;deps.svg&quot; width=&quot;30%&quot;&gt;
&lt;p&gt;This language describes a directed graph of dependencies. I.e. they say what depends on what, or what must be built before you can build something else. Targets and dependencies are completely separate from what lives inside them, which are tasks.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-procedural-language&quot;&gt;The procedural language&lt;&#x2F;h3&gt;
&lt;p&gt;The procedural language is a list of tasks:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target ...&amp;gt;
    &amp;lt;javac ...&amp;gt;
    &amp;lt;copy ...&amp;gt;
    &amp;lt;zip ...&amp;gt;
    &amp;lt;junit ...&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;When the dependency mechanism has decided a target will be executed, its tasks are executed one by one in order, just like in a programming language. Except that tasks live inside targets, they are completely separate from them. Essentially each target has a little program inside it consisting of tasks, and these tasks are a conventional programming language, nothing special (except for the lack of basic looping and branching constructs).&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m sorry if the above is glaringly obvious to you, but it only recently became clear to me, and it helped me a lot to think about how to improve my Ant files.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;avoiding-repeated-code&quot;&gt;Avoiding repeated code&lt;&#x2F;h2&gt;
&lt;p&gt;Imagine you have two similar Ant targets:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;A&quot;&amp;gt;
    &amp;lt;javac
        srcdir=&quot;a&#x2F;src&quot; destdir=&quot;a&#x2F;bin&quot;
        classpath=&quot;myutil.jar&quot; debug=&quot;false&quot;
    &#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;B&quot;&amp;gt;
    &amp;lt;javac
        srcdir=&quot;b&#x2F;code&quot; destdir=&quot;b&#x2F;int&quot;
        classpath=&quot;myutil.jar&quot; debug=&quot;false&quot;
    &#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;The classpath and debug information are the same in both targets, and we would like to write this information in one single place. Imagine with me that the code we want to share is too complex for it to be possible to store it as the values of properties in some properties file.&lt;&#x2F;p&gt;
&lt;p&gt;How do we share this code?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-wrong-way-antcall&quot;&gt;The Wrong Way: antcall&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s the solution we were using in my project until I discovered the right way:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;compile&quot;&amp;gt;
    &amp;lt;javac
        srcdir=&quot;${srcdir}&quot; destdir=&quot;${destdir}&quot;
        classpath=&quot;myutil.jar&quot; debug=&quot;false&quot;
    &#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;A&quot;&amp;gt;
    &amp;lt;antcall target=&quot;compile&quot;&amp;gt;
        &amp;lt;param name=&quot;srcdir&quot; value=&quot;a&#x2F;src&quot;&#x2F;&amp;gt;
        &amp;lt;param name=&quot;destdir&quot; value=&quot;a&#x2F;bin&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;antcall&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;B&quot;&amp;gt;
    &amp;lt;antcall target=&quot;compile&quot;&amp;gt;
    ...
&lt;&#x2F;pre&gt;
&lt;p&gt;Here we put the shared code into a target called compile, which makes use of properties to access the varying information (or the parameters, if we think of this as a function). The targets A and B use the &amp;lt;antcall&amp;gt; task to launch the compile target, setting the values of the relevant properties.&lt;&#x2F;p&gt;
&lt;p&gt;This works, so why is it Wrong?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-not-antcall&quot;&gt;Why not antcall?&lt;&#x2F;h3&gt;
&lt;p&gt;antcall launches a whole new Ant process and runs the supplied target within that. This is wrong because it subverts the way Ant is supposed to work. The new process will re-calculate all the dependencies in the project (even if our target doesn&#x27;t depend on anything) which could be slow. Any dependencies of the compile target will be run even if they&#x27;ve already been run, meaning some of your assumptions about order of running could be incorrect, and the assumption that each target will only run once will be violated. What&#x27;s more, it subverts the Ant concept that properties are immutable, and remain set once you&#x27;ve set them: in the example above, srcdir and destdir will have different values at different times (because they exist inside different Ant processes).&lt;&#x2F;p&gt;
&lt;p&gt;Basically what we&#x27;re doing here is breaking all of Ant&#x27;s paradigms to force it to do what we want. Before Ant 1.8 you could have considered it a necessary evil. Now, it&#x27;s just Evil.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-horrific-way-custom-tasks&quot;&gt;The Horrific Way: custom tasks&lt;&#x2F;h3&gt;
&lt;p&gt;Ant allows you to write your own tasks (not targets) in Java. So our example would look something like this:&lt;&#x2F;p&gt;
&lt;p&gt;Java:&lt;&#x2F;p&gt;
&lt;pre&gt;public class MyCompile extends Task {
    public void execute() throws BuildException
    {
        Project p = getProject();

        Javac javac = new Javac();

        javac.setSrcdir(  new Path( p, p.getUserProperty( &quot;srcdir&quot; ) ) );
        javac.setDestdir( new File( p.getUserProperty( &quot;destdir&quot; ) ) );
        javac.setClasspath( new Path( p, &quot;myutil.jar&quot; ) );
        javac.setDebug( false );
        javac.execute();
    }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Ant:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;first&quot;&amp;gt;
    &amp;lt;javac srcdir=&quot;mycompile&quot;&#x2F;&amp;gt;
    &amp;lt;taskdef name=&quot;mycompile&quot; classname=&quot;MyCompile&quot;
        classpath=&quot;mycompile&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;A&quot; depends=&quot;first&quot;&amp;gt;
    &amp;lt;mycompile&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;B&quot; depends=&quot;first&quot;&amp;gt;
    &amp;lt;mycompile&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Here we write the shared code as a Java task, then call that task from inside targets A and B. The only word to describe this approach is &quot;cumbersome&quot;. Not only do we need to ensure our code gets compiled before we try to use it, and add a taskdef to allow Ant to see our new task (meaning every target gets a new dependency on the &quot;first&quot; target), but much worse, our re-used code has to be written in Java, rather than the Ant syntax we&#x27;re using for everything else. At this point you might start asking yourself why you&#x27;re using Ant at all - my thoughts start drifting towards writing my own build scripts in Java ... anyway, I&#x27;m sure that would be a very bad idea.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-relatively-ok-way-macrodef&quot;&gt;The Relatively OK Way: macrodef&lt;&#x2F;h3&gt;
&lt;p&gt;So, enough teasing. Here&#x27;s the Right Way:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;macrodef name=&quot;mycompile&quot;&amp;gt;
    &amp;lt;attribute name=&quot;srcdir&quot;&#x2F;&amp;gt;
    &amp;lt;attribute name=&quot;destdir&quot;&#x2F;&amp;gt;
    &amp;lt;sequential&amp;gt;
        &amp;lt;javac
            srcdir=&quot;@{srcdir}&quot; destdir=&quot;@{destdir}&quot;
            classpath=&quot;myutil.jar&quot; debug=&quot;false&quot;
        &#x2F;&amp;gt;
    &amp;lt;&#x2F;sequential&amp;gt;
&amp;lt;&#x2F;macrodef&amp;gt;

&amp;lt;target name=&quot;A&quot;&amp;gt;
    &amp;lt;mycompile srcdir=&quot;a&#x2F;src&quot; destdir=&quot;a&#x2F;bin&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;

&amp;lt;target name=&quot;B&quot;&amp;gt;
    &amp;lt;mycompile srcdir=&quot;b&#x2F;code&quot; destdir=&quot;b&#x2F;int&quot;&#x2F;&amp;gt;
&amp;lt;&#x2F;target&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Since Ant 1.8, we have the macrodef task, which allows us to write our own tasks in Ant syntax. In any other language these would be called functions, with arguments which Ant calls attributes. You use these attributes by giving their name wrapped in a @{} rather than the normal ${} for properties. The body of the function lives inside a sequential tag.&lt;&#x2F;p&gt;
&lt;p&gt;This allows us to write re-usable tasks within Ant. But what about re-using parts from the other language - the declarative targets and dependencies?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;avoiding-repeated-dependencies&quot;&gt;Avoiding repeated dependencies?&lt;&#x2F;h2&gt;
&lt;p&gt;Imagine we have a build file containing targets like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;target name=&quot;everyoneneedsme&quot;...

&amp;lt;target name=&quot;A&quot; depends=&quot;everyoneneedsme&quot;...
&amp;lt;target name=&quot;B&quot; depends=&quot;everyoneneedsme&quot;...
&amp;lt;target name=&quot;C&quot; depends=&quot;everyoneneedsme&quot;...
&amp;lt;target name=&quot;D&quot; depends=&quot;everyoneneedsme&quot;...
&lt;&#x2F;pre&gt;
&lt;p&gt;In Ant, I don&#x27;t know how to share this. The best I can do is make a single target that is re-used whenever I want the same long list of dependencies, but in a situation like this where everything needs to depend on something, I don&#x27;t know what to do. (Except, of course, drop to the Nuclear Option of the &amp;lt;script&amp;gt; tag, which we&#x27;ll see next time.)&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t used it in anger, but this kind of thing seems pretty straightforward with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gradle.org&#x2F;&quot;&gt;Gradle&lt;&#x2F;a&gt;. I believe the following is roughly equivalent to my example above, but I hope someone will correct me if I get it wrong:&lt;&#x2F;p&gt;
&lt;pre&gt;task everyoneneedsme

tasks.whenTaskAdded { task -&amp;gt;
    task.dependsOn everyoneneedsme
}

task A
task B
task C
task D
&lt;&#x2F;pre&gt;
&lt;p&gt;(Disclaimer: I haven&#x27;t run this.)&lt;&#x2F;p&gt;
&lt;p&gt;So, if you want nice features in your build tool, like code-reuse and testability, you should consider a build tool that is integrated into a grown-up programming language where all this stuff comes for free. But, if you&#x27;re stuck with Ant, you should not despair: basic good practice is possible if you make the effort.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ti
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;BTW, &quot;proscribe&quot; and &quot;prescribe&quot; are two very different words.  I think you wanted the second but wrote the first by mistake.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-08-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Ti, I fixed the pr[oe]scribe problem.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Gilbert
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Good point about antcall – scripts using antcall are a PITA !&lt;br&#x2F;&gt;
btw. The Macrodef Task exists since Ant 1.6.0 &lt;sup&gt;12&lt;&#x2F;sup&gt;⁄&lt;sub&gt;2003&lt;&#x2F;sub&gt;, see &lt;a href=&quot;http:&#x2F;&#x2F;ant.apache.org&#x2F;manual&#x2F;Tasks&#x2F;macrodef.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;ant.apache.org&#x2F;manual&#x2F;Tasks&#x2F;macrodef.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-07-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Gilbert, thanks for the correction.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Dry run mode for Ant (ant -n, ant --dry-run)</title>
          <pubDate>Thu, 31 Jan 2013 16:47:57 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/01/31/dry-run-mode-for-ant-ant-n-ant-dry-run/</link>
          <guid>https://artificialworlds.net/blog/2013/01/31/dry-run-mode-for-ant-ant-n-ant-dry-run/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/01/31/dry-run-mode-for-ant-ant-n-ant-dry-run/">&lt;p&gt;I am working on the problem of writing Ant build files in a test-driven way. One thing I found myself needing was a &quot;dry run&quot; mode, like many Unix tools have. For example, make has the -n or --dry-run option, which shows what it would have done, but doesn&#x27;t really do it.&lt;&#x2F;p&gt;
&lt;p&gt;Today I found a partial solution to this problem, so that you can at least see which dependencies will be run when you run a particular ant target.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s an horrific hack, but it&#x27;s the best I can do at the moment.&lt;&#x2F;p&gt;
&lt;p&gt;We write some code in a &amp;lt;script&amp;gt; tag to hack all the targets in our project (at runtime). We modify the targets so they all have an &quot;unless&quot; attribute, set to a property name of &quot;DRY-RUN&quot;. Then we set the &quot;DRY-RUN&quot; property, and execute our target.&lt;&#x2F;p&gt;
&lt;p&gt;Ant prints out the names of all the targets in the dependency chain, even if they are not executed because of an unless attribute.&lt;&#x2F;p&gt;
&lt;p&gt;Note: this code makes use of the Ant &amp;lt;script&amp;gt; script tag, which is an Ant 1.8+ feature. Using JavaScript inside this tag seems to be supported in Oracle, OpenJDK and IBM versions of Java, but is not guaranteed.&lt;&#x2F;p&gt;
&lt;pre&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project default=&quot;build&quot;&amp;gt;

    &amp;lt;target name=&quot;targetA&quot;&#x2F;&amp;gt;
    &amp;lt;target name=&quot;targetB&quot; depends=&quot;targetA&quot;&amp;gt;
        &amp;lt;echo message=&quot;DON&#x27;T RUN ME&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;target&amp;gt;
    &amp;lt;target name=&quot;targetC&quot; depends=&quot;targetB&quot;&#x2F;&amp;gt;

    &amp;lt;target name=&quot;build&quot; depends=&quot;targetB&quot;&#x2F;&amp;gt;

    &amp;lt;target name=&quot;dry-run&quot;&amp;gt;
        &amp;lt;do-dry-run target=&quot;build&quot;&#x2F;&amp;gt;
    &amp;lt;&#x2F;target&amp;gt;

    &amp;lt;macrodef name=&quot;do-dry-run&quot;&amp;gt;
        &amp;lt;attribute name=&quot;target&quot;&#x2F;&amp;gt;
        &amp;lt;sequential&amp;gt;
            &amp;lt;script language=&quot;javascript&quot;&amp;gt;&amp;lt;![CDATA[

                var targs = project.getTargets().elements();
                while( targs.hasMoreElements() ) {
                    var targ = targs.nextElement();
                    targ.setUnless( &quot;DRY.RUN&quot; );
                }
                project.setProperty( &quot;DRY.RUN&quot;, &quot;1&quot; );
                project.executeTarget( &quot;@{target}&quot; );

            ]]&amp;gt;&amp;lt;&#x2F;script&amp;gt;
        &amp;lt;&#x2F;sequential&amp;gt;
    &amp;lt;&#x2F;macrodef&amp;gt;

&amp;lt;&#x2F;project&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Running this build file normally, the tasks in the targets execute, so we can see that the &amp;lt;echo&amp;gt; happens:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ant
Buildfile: build.xml

targetA:

targetB:
     [echo] DON&#x27;T RUN ME

build:

BUILD SUCCESSFUL
Total time: 0 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;But when we run the dry-run target, only the target names are printed, and the &amp;lt;echo&amp;gt; task (and any other tasks) don&#x27;t:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ant dry-run
Buildfile: build.xml

dry-run:

targetA:

targetB:

build:

BUILD SUCCESSFUL
Total time: 0 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;A lot of pain, for a partial implementation of very simple functionality that you&#x27;d expect to be a built-in feature? I couldn&#x27;t possibly comment.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Russel Winder
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Isn’t the real problem here that you are using Ant? Perhaps try a modern build tool such as Gradle? (&lt;a href=&quot;http:&#x2F;&#x2F;www.gradle.org&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.gradle.org&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Russell, yes it is.  But, in case others are similarly doomed, I hope this might help them.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 12 &quot; Scoring, done!</title>
          <pubDate>Wed, 16 Jan 2013 08:19:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/01/16/my-first-raspberry-pi-game-part-12-scoring-done/</link>
          <guid>https://artificialworlds.net/blog/2013/01/16/my-first-raspberry-pi-game-part-12-scoring-done/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/01/16/my-first-raspberry-pi-game-part-12-scoring-done/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, 12.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Today, we finish!&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 12 -- Scoring, done!&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;20abba92-0d0f-47ce-953b-6e29cd996a19&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Our game is almost done. All we need to do now is let you play several times, and give you a score at the end.&lt;&#x2F;p&gt;
&lt;p&gt;First, because we&#x27;re going to use it lots of times, we need to make the ready_screen function set its background colour properly. Open redgreen.py in LeafPad, and add a single line to the function ready_screen, making it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen():
    &lt;span style=&quot;color: green;&quot;&gt;screen.fill( pygame.Color( &quot;black&quot; ) )&lt;&#x2F;span&gt;
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Ready?&quot;, white, True )
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;Previously, ready_screen was always the first thing we did, so we got away with not drawing a background colour because it starts off plain black. Now, we need to do it.&lt;&#x2F;p&gt;
&lt;p&gt;Next, let&#x27;s do the really interesting part. We want to play the game several times, and whenever we want to do something several times, we need a loop. This time we&#x27;ll use a for loop, letting us go through a list of things. Scroll to the very bottom, and change the code to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;# We start from here&lt;&#x2F;span&gt;

start()

&lt;span style=&quot;color: green;&quot;&gt;for i in range( 10 ):&lt;&#x2F;span&gt;

    &lt;span style=&quot;color: blue;&quot;&gt;ready_screen()&lt;&#x2F;span&gt;

    &lt;span style=&quot;color: blue;&quot;&gt;wait()&lt;&#x2F;span&gt;

    &lt;span style=&quot;color: blue;&quot;&gt;shape()&lt;&#x2F;span&gt;

end()
&lt;&#x2F;pre&gt;
&lt;p&gt;The new lines are green above, and lines that haven&#x27;t changed except being indented by putting four spaces at the beginning are blue.&lt;&#x2F;p&gt;
&lt;p&gt;A for loop lets you run through a list of things, running the same code each time. A for loop always looks like for NAME in LIST where NAME is the name of a new variable, and LIST is a list of things. What we&#x27;ve done here is make a list of 10 numbers by calling the range function and giving it an argument of 10, and told Python to put the particular item of the list that we&#x27;re working on now into a variable called i.&lt;&#x2F;p&gt;
&lt;p&gt;So, the ready_screen, wait and shape functions will each get called 10 times. Each time they are called, i will be a different number. We&#x27;re not using i yet, so all that matters for the moment is that the code runs 10 times. Try it out by opening LXTerminal and typing .&#x2F;redgreen.py, and you&#x27;ll see that you can play the game 10 times, and then it will finish.&lt;&#x2F;p&gt;
&lt;p&gt;Playing 10 times is all very well, but it&#x27;s not a lot of fun if I can&#x27;t see how well I&#x27;ve done at the end. Let&#x27;s keep track of our score.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll award the player 1 point for every time they get it right, and no points if they get it wrong. The places where we know which of these has happened are in red_shape and green_shape. Let&#x27;s change them to pass back a score (either 1 or 0) depending on what you did:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_shape():
    &lt;span style=&quot;color: blue; font-style:italic;&quot;&gt;...the rest of green_shape is still here...&lt;&#x2F;span&gt;

    pressed = shape_wait()

    if pressed:
        green_success()
        &lt;span style=&quot;color: green;&quot;&gt;return 1&lt;&#x2F;span&gt;
    else:
        green_failure()
        &lt;span style=&quot;color: green;&quot;&gt;return 0&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;pre&gt;def red_shape():
    &lt;span style=&quot;color: blue; font-style:italic;&quot;&gt;...the rest of green_shape is still here...&lt;&#x2F;span&gt;

    pressed = shape_wait()

    if pressed:
        red_failure()
        &lt;span style=&quot;color: green;&quot;&gt;return 0&lt;&#x2F;span&gt;
    else:
        red_success()
        &lt;span style=&quot;color: green;&quot;&gt;return 1&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve abbreviated it above, but we&#x27;re not changing anything in these functions except at the very bottom, where we&#x27;re adding two return lines to each function.&lt;&#x2F;p&gt;
&lt;p&gt;Whenever the player succeeds, we return a score of 1 point, and whenever they fail we return 0 points.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re not doing anything with this score yet. We call the green_shape and red_shape functions from inside shape, so first let&#x27;s make sure shape passes back the answer to where we need it:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape():
    GREEN = 0
    RED   = 1
    shape = random.choice( [GREEN, RED] )

    if shape == GREEN:
        &lt;span style=&quot;color: green;&quot;&gt;return&lt;&#x2F;span&gt; green_shape()
    else:
        &lt;span style=&quot;color: green;&quot;&gt;return&lt;&#x2F;span&gt; red_shape()
&lt;&#x2F;pre&gt;
&lt;p&gt;shape doesn&#x27;t need to do anything special here - just take the answer coming from green_shape or red_shape and use the return statement to pass it back to us.&lt;&#x2F;p&gt;
&lt;p&gt;Now shape is giving us back an answer, we can use it in the main code right at the bottom:&lt;&#x2F;p&gt;
&lt;pre&gt;start()

&lt;span style=&quot;color:green;&quot;&gt;correct = 0&lt;&#x2F;span&gt;

for i in range( 10 ):

    ready_screen()

    wait()

    &lt;span style=&quot;color:green;&quot;&gt;correct += &lt;&#x2F;span&gt;shape()

end( &lt;span style=&quot;color:green;&quot;&gt;correct&lt;&#x2F;span&gt; )
&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ve made a variable called correct that keeps hold of how many correct answers we&#x27;ve been given (i.e. the score). It starts off as zero, and every time we call shape we add on the answer that comes back. shape will either return 0 or 1, so correct will increase by either 0 or 1 each time.&lt;&#x2F;p&gt;
&lt;p&gt;The last thing we&#x27;ve done here is pass the answer (the player&#x27;s final score) into the end function so we can display it. To use this answer, we need to change end a bit:&lt;&#x2F;p&gt;
&lt;pre&gt;def end( &lt;span style=&quot;color:green;&quot;&gt;correct&lt;&#x2F;span&gt; ):
    &lt;span style=&quot;color:green;&quot;&gt;print &quot;You got %d correct answers&quot; % correct&lt;&#x2F;span&gt;
    screen.fill( pygame.Color( &quot;black&quot; ) )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Thanks for playing!&quot;, white, True )
    &lt;span style=&quot;color:green;&quot;&gt;msg = &quot;Score: %d   Press a key to exit&quot; % correct&lt;&#x2F;span&gt;
    write_text( screen, &lt;span style=&quot;color:green;&quot;&gt;msg&lt;&#x2F;span&gt;, white, False )
    pygame.display.flip()
    pygame.event.clear()
    timed_wait( 0, press_events )
&lt;&#x2F;pre&gt;
&lt;p&gt;We changed the def line to allow us to pass in the score, giving it the same name we used below, correct. Then we added a line that prints out the answer into the terminal, just for good measure, and we modified the write_text line, splitting it into 2 parts - creating a variable called msg containing our message, and then using it on the next line.&lt;&#x2F;p&gt;
&lt;p&gt;Twice above we&#x27;ve used a nice feature of Python that makes building our own messages quite simple. If you write a string like &quot;Score: %d Press a key to exit&quot; you can substitute a number into it using the % &quot;operator&quot; as we&#x27;ve done (an operator is something like + or &#x2F; that combines 2 things). Where the %d appears in the string, it gets replaced by the number inside the variable you supply (correct in our case). You can also substitute in other strings (using %s) and lots of other things if you want to. This allows us to put the score into a string and then print it on the screen.&lt;&#x2F;p&gt;
&lt;p&gt;If you try your game now you will see it counts how many right answers you got and tells you at the end. Wouldn&#x27;t it be better, though, if it told you how you were doing all the way through?&lt;&#x2F;p&gt;
&lt;p&gt;Scroll up to the ready_screen function and modify it to take two arguments and use them to keep us informed:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen( &lt;span style=&quot;color: green;&quot;&gt;go_number, correct&lt;&#x2F;span&gt; ):
    screen.fill( pygame.Color( &quot;black&quot; ) )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Ready?&quot;, white, True )

    &lt;span style=&quot;color: green;&quot;&gt;go_number_str = &quot;Turn: %d    Score: %d&quot; % ( ( go_number + 1 ), correct )&lt;&#x2F;span&gt;

    &lt;span style=&quot;color: green;&quot;&gt;write_text( screen, go_number_str, pygame.Color( &quot;white&quot; ), False )&lt;&#x2F;span&gt;

    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;The arguments we take are called go_number and correct. correct will be the current score, as we&#x27;ve seen before, and go_number is the counter telling us how far we&#x27;ve got.&lt;&#x2F;p&gt;
&lt;p&gt;We use a slightly different form of the % operator here to substitute two values into a string instead of one. To do this, we put a list of values on the right instead of just one: ( ( go_number + 1 ), correct ). We need brackets around the outside so that Python knows it is a list and doesn&#x27;t just take the first value on its own. When we use a list like this, the values will be substituted in order, one for each %d (or %s or similar) that is in the string. You must always have the same number of %ds in the string as values in the list.&lt;&#x2F;p&gt;
&lt;p&gt;You may be wondering why we have to add one to go_number. We&#x27;ll see in a moment.&lt;&#x2F;p&gt;
&lt;p&gt;To be able to provide the two new arguments to ready_screen we need to change the code right at the bottom to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;start()

correct = 0

for i in range( 10 ):

    ready_screen( &lt;span style=&quot;color:green;&quot;&gt;i, correct&lt;&#x2F;span&gt; )

    wait()

    correct += shape()

end( correct )
&lt;&#x2F;pre&gt;
&lt;p&gt;Remember when we made the for loop I mentioned that i would be a different number each time we ran the code inside the loop? We pass that number in to ready_screen where it will be used as the go_number. We also pass in the current score, correct.&lt;&#x2F;p&gt;
&lt;p&gt;The reason why we needed to add 1 to go_number inside ready_screen is that when you have a loop like for i in range( 10 ), the variable i actually gets the values 0, 1, 2, ... with the last value being 9, instead of ranging from 1 to 10 as you might expect. The reasoning behind this is kind of lost in the mists of time, and kind of makes perfect sense, depending how you look at it. Anyway, believe me when I tell you that once you&#x27;ve got used to it you&#x27;re going to find it warm and comforting, but for now you may find it a bit weird.&lt;&#x2F;p&gt;
&lt;p&gt;And, on that typically strange note, we have finished! Try out your program, and you should find it tells you what go you&#x27;re on, and what your score is all the way through.&lt;&#x2F;p&gt;
&lt;p&gt;Something else you might like to do now is make your game run in full-screen mode (like many games). You can do that by changing the start function like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def start():
    global screen
    pygame.init()
    screen = pygame.display.set_mode( screen_size&lt;span style=&quot;color:green;&quot;&gt;, pygame.FULLSCREEN&lt;&#x2F;span&gt; )
&lt;&#x2F;pre&gt;
&lt;p&gt;If you have any problems, compare your version with mine here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-12&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve made a slightly extended version of the game that measures your reaction speed and gives you a score based on how quickly you press. In future I may even add more features. If you&#x27;d like to follow the project, you can find it here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;redgreen&quot;&gt;redgreen on github&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll be doing more series in the future, some for beginners like this one, and some more advanced topics. If you&#x27;d like to find out what I&#x27;m doing, subscribe to the blog &lt;a href=&quot;&#x2F;blog&#x2F;feed&#x2F;&quot;&gt;RSS feed&lt;&#x2F;a&gt;, follow me on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;twitter.com&#x2F;#!&#x2F;andybalaam&quot;&gt;Twitter&lt;&#x2F;a&gt; or go to my &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.youtube.com&#x2F;user&#x2F;ajbalaam&quot;&gt;YouTube page&lt;&#x2F;a&gt; and subscribe.&lt;&#x2F;p&gt;
&lt;iframe id=&quot;fr&quot; src=&quot;http:&#x2F;&#x2F;www.youtube.com&#x2F;subscribe_widget?p=ajbalaam&quot; style=&quot;overflow: hidden; height: 105px; width:  300px; border: 0;&quot; scrolling=&quot;no&quot; frameborder=&quot;0&quot;&gt;&lt;&#x2F;iframe&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 11 &quot; Being less rude</title>
          <pubDate>Fri, 11 Jan 2013 08:14:59 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2013/01/11/my-first-raspberry-pi-game-part-11-being-less-rude/</link>
          <guid>https://artificialworlds.net/blog/2013/01/11/my-first-raspberry-pi-game-part-11-being-less-rude/</guid>
          <description xml:base="https://artificialworlds.net/blog/2013/01/11/my-first-raspberry-pi-game-part-11-being-less-rude/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, 11, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 11 -- Being less rude&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;b6edf3d9-be14-48a8-a496-9a027926d9dc&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;ve nearly finished our game. Next on our list is to fix that bug where you can&#x27;t exit some of the time, and make our code a bit tidier in the process.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing I want to do is make the &lt;strong&gt;Esc&lt;&#x2F;strong&gt; key quit the game. This is fairly normal behaviour, and will help if we run in full screen mode, where there is no close button to click.&lt;&#x2F;p&gt;
&lt;p&gt;Open up redgreen.py in LeafPad as usual, and find the function shape_wait and the line if evt.type == pygame.QUIT:. Replace the whole line with this:&lt;&#x2F;p&gt;
&lt;pre&gt;        if is_quit( evt ):
&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ve replaced the code asking whether the event was a quit event (i.e. the user closed the window) with a call to a function. Let&#x27;s write that function. Just above shape_wait type in this function:&lt;&#x2F;p&gt;
&lt;pre&gt;def is_quit( evt ):
    return evt.type == pygame.QUIT
&lt;&#x2F;pre&gt;
&lt;p&gt;You&#x27;ve just done another bit of refactoring. Instead of writing the code directly in the if line, we&#x27;ve added a call to the function, and the function does exactly the same thing as we did before: it returns True if the event is a quit event, and False otherwise. If you try the program now (by saving in LeafPad, opening LXTerminal and typing .&#x2F;redgreen.py) you should see it behaves exactly as it did before.&lt;&#x2F;p&gt;
&lt;p&gt;You may well ask why we did it. The answer is because now we can change the is_quit function to do something extra. Replace it with this:&lt;&#x2F;p&gt;
&lt;pre&gt;def is_quit( evt ):
    return (
        evt.type == pygame.QUIT or
        (
            evt.type == pygame.KEYDOWN and
            evt.key == pygame.K_ESCAPE
        )
    )
&lt;&#x2F;pre&gt;
&lt;p&gt;This is a more complicated bit of logic, saying that we will return True if either of two things is true: EITHER the event is a quit event (as before), OR the event is a KEYDOWN, and the specific key that was pressed was the Escape key (&quot;Esc&quot;) on the keyboard. Notice that the brackets around the &quot;and&quot; part help us know which bits go together - we don&#x27;t want to quit for any keypress event, only one where the key is Escape.&lt;&#x2F;p&gt;
&lt;p&gt;If you try your program again, you should find you can press Escape to exit when you&#x27;re looking at a red or green shape.&lt;&#x2F;p&gt;
&lt;p&gt;The shape_wait function is quite a useful one, and the next thing we want to do is use it in a few more places. Before we can do that, we need to refactor it to make it a bit more flexible.&lt;&#x2F;p&gt;
&lt;p&gt;Make a new function called timed_wait further up, just before start, and cut the entire body of shape_wait and paste it into timed_wait. So it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def timed_wait():
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    &lt;span style=&quot;color: blue;&quot;&gt;... all the rest of shape_wait here ...&lt;&#x2F;span&gt;
    pygame.time.set_timer( finished_waiting_event_id, 0 )
&lt;&#x2F;pre&gt;
&lt;p&gt;Now change shape_wait to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape_wait():
    &quot;&quot;&quot;
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    &quot;&quot;&quot;
    return timed_wait()
&lt;&#x2F;pre&gt;
&lt;p&gt;As usual, we&#x27;ve just replaced some code with a call to a function that contains the exact same code, so hopefully our program will work exactly as before.&lt;&#x2F;p&gt;
&lt;p&gt;Now we&#x27;re going to make timed_wait a bit more general, while still preserving all the same behaviour. We do this by changing some of the variables we use in timed_wait into arguments we pass in. Change shape_wait to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape_wait():
    &quot;&quot;&quot;
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    &quot;&quot;&quot;
    press_events = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    return timed_wait( 2000, press_events ) # 2 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;and modify timed_wait to accept those arguments (notice I also added a description of what it does):&lt;&#x2F;p&gt;
&lt;pre&gt;def timed_wait( time_to_wait, event_types_that_cancel ):
    &quot;&quot;&quot;
    Wait for time_to_wait, but cancel if a relevant event happens.
    Return True if cancelled, or False if we waited the full time.
    &quot;&quot;&quot;

    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    pygame.event.clear()

    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if is_quit( evt ):
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed
&lt;&#x2F;pre&gt;
&lt;p&gt;Again, after these changes our program should work exactly as before - we&#x27;re passing values in as arguments that are exactly what we used to make as variables. But now, timed_wait is a lot more flexible, and we&#x27;ll use that flexibility very soon.&lt;&#x2F;p&gt;
&lt;p&gt;But first, we need to make a change to cover the unexpected. Inside timed_wait we&#x27;ve made a timer using pygame.time.set_timer and at the end we&#x27;ve cancelled it by calling pygame.time.set_timer again. However, if something goes wrong in between where we create the timer, and where we cancel it, it&#x27;s possible that something called an &quot;exception&quot; will be &quot;thrown&quot;. When an exception is thrown, the program stops running normally, line by line, and jumps out to somewhere else.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not going to explain any more about exceptions here, but I am going to show you how to make absolutely sure that something will happen, even if an exception is thrown. The way to do that is to use a try ... finally block. We want to make sure our timer is always cancelled, so as soon as we&#x27;ve made it, we start a try block, and at the end we say finally. Anything inside that finally block will be run, even if an exception was thrown in the code inside the try block. The changes look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def timed_wait( time_to_wait, event_types_that_cancel ):
    &quot;&quot;&quot;
    Wait for time_to_wait, but cancel if a relevant event happens.
    Return True if cancelled, or False if we waited the full time.
    &quot;&quot;&quot;

    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    &lt;span style=&quot;color: green;&quot;&gt;try:&lt;&#x2F;span&gt;
        &lt;span style=&quot;color: blue;&quot;&gt;pygame.event.clear()

        pressed = False
        waiting = True
        while waiting:
            evt = pygame.event.wait()
            if is_quit( evt ):
                quit()
            elif evt.type in event_types_that_cancel:
                waiting = False
                pressed = True
            elif evt.type == finished_waiting_event_id:
                waiting = False&lt;&#x2F;span&gt;
    &lt;span style=&quot;color: green;&quot;&gt;finally:&lt;&#x2F;span&gt;
        &lt;span style=&quot;color: blue;&quot;&gt;pygame.time.set_timer( finished_waiting_event_id, 0 )&lt;&#x2F;span&gt;

    return pressed
&lt;&#x2F;pre&gt;
&lt;p&gt;The lines in green are new, and the ones in blue are just indented by four more spaces to make them part of the try and finally blocks. Now, we know that even if something goes wrong while we&#x27;re waiting, we will always cancel the timer we set up. Yet more good manners!&lt;&#x2F;p&gt;
&lt;p&gt;Now, after all that work, we finally have a timed_wait function that is flexible enough to be used everywhere we want to wait for something. Let&#x27;s start with the wait function. Change it to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def wait():
    time_to_wait = random.randint( 1500, 3000 ) # Between 1.5 and 3 seconds
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( time_to_wait, () )&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;By using our clever timed_wait function instead of the built-in pygame.time.wait we gain some extra politeness: we can now quit the program on the &quot;Ready?&quot; screen by closing the window or pressing the Escape key. Try it!&lt;&#x2F;p&gt;
&lt;p&gt;Notice that we passed in () as the second argument to timed_wait. This argument is called event_types_that_cancel and is normally a list of types of event that will stop us waiting. () is Python&#x27;s way of saying an empty list, so we&#x27;re saying we don&#x27;t want to stop for any normal events (such as key presses) - only for quit events, or when the time is up.&lt;&#x2F;p&gt;
&lt;p&gt;Before we change lots more code to use timed_wait, we are going to make a new variable that we can use in lots of places in the code. Quite a few times, we want to wait for either a key press or a mouse click. We want this when we&#x27;re showing a red or green square, and when we&#x27;re at the end saying goodbye, and ideally we also want it when we&#x27;re telling the user how they did, so they can skip it if they&#x27;re impatient. So, right near the top, add a new line just below where we create screen_size:&lt;&#x2F;p&gt;
&lt;pre&gt;screen_width = 640
screen_height = 480
screen_size = screen_width, screen_height
&lt;span style=&quot;color: green;&quot;&gt;press_events = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;This variable press_events will be our list of normal event types that we consider to be a &quot;press&quot; - essentially, the player doing something. Now that we&#x27;ve defined this at the top, we can take out the variable with the same name from shape_wait - it will use the global one instead:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape_wait():
    &quot;&quot;&quot;
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    &quot;&quot;&quot;
    return timed_wait( 2000, press_events ) # 2 seconds
&lt;&#x2F;pre&gt;
&lt;p&gt;We can also re-use press_events in the end function, and at the same time call our new timed_wait function:&lt;&#x2F;p&gt;
&lt;pre&gt;def end():
    screen.fill( pygame.Color( &quot;black&quot; ) )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Thanks for playing!&quot;, white, True )
    write_text( screen, &quot;Press a key to exit&quot;, white, False )
    pygame.display.flip()
    pygame.event.clear()
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( 0, press_events )&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that this time we pass zero as the time to wait - this just means we will never time out on this screen - the zero gets passed in and used in the pygame.time.set_timer call, but passing in zero for the time there means &quot;cancel this event&quot;, and is harmless if the event doesn&#x27;t actually exist, so no timer will be set up - we will only stop waiting when the player presses something, which is what we want here.&lt;&#x2F;p&gt;
&lt;p&gt;Now we can make our success and failure functions more polite. Change them all to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_success():
    tick()
    green = pygame.Color( &quot;green&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Well done!&quot;, green, True )
    write_text( screen, &quot;You pressed on green!&quot;, white, False )
    pygame.display.flip()
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( 2000, press_events ) # 2 seconds&lt;&#x2F;span&gt;

def green_failure():
    cross()
    red   = pygame.Color( &quot;red&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Bad Luck!&quot;, red, True )
    write_text( screen, &quot;Green means press something!&quot;, white, False )
    pygame.display.flip()
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( 2000, press_events ) # 2 seconds&lt;&#x2F;span&gt;

def red_success():
    tick()
    green = pygame.Color( &quot;green&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Well done!&quot;, green, True )
    write_text( screen, &quot;You didn&#x27;t press on red!&quot;, white, False )
    pygame.display.flip()
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( 2000, press_events ) # 2 seconds&lt;&#x2F;span&gt;

def red_failure():
    cross()
    red   = pygame.Color( &quot;red&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Bad Luck!&quot;, red, True )
    write_text( screen, &quot;Red means don&#x27;t press anything!&quot;, white, False )
    pygame.display.flip()
    &lt;span style=&quot;color: green;&quot;&gt;timed_wait( 2000, press_events ) # 2 seconds&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;They all call timed_wait saying wait for 2 seconds, but skip if a key is pressed because the player is impatient to get on to the next round. This change means not only can you skip past these success and failure screens, but also you can quit while they are visible, and the last vestige of rudeness has been wiped out from our game.&lt;&#x2F;p&gt;
&lt;p&gt;Well done - just one job left, which is to allow several rounds, and count the player&#x27;s score as they play. We&#x27;ll do that next time.&lt;&#x2F;p&gt;
&lt;p&gt;In the meantime, you can fix a bug I made - I typed get_width instead of get_height, which made my circles too big. Change the line inside green_shape that looks like radius = screen.get_width() &#x2F; 3 to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;    radius = screen.get_height() &#x2F; 3
&lt;&#x2F;pre&gt;
&lt;p&gt;There we are - much better&lt;&#x2F;p&gt;
&lt;p&gt;You can check your verson against mine here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-11&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;See you next time, when hopefully we&#x27;ll finish the game!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    mason
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;in part 12 mine wold not work so i copied and pasted yours but it did not work?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi mason, what error or wrong behaviour did you see?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 10 &quot; Red square</title>
          <pubDate>Fri, 14 Dec 2012 23:31:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/12/14/my-first-raspberry-pi-game-part-10-red-square/</link>
          <guid>https://artificialworlds.net/blog/2012/12/14/my-first-raspberry-pi-game-part-10-red-square/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/12/14/my-first-raspberry-pi-game-part-10-red-square/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, 10, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 10 -- Red square&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;8d47d6c1-79af-4c14-b18b-564ea7ea49bb&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;re writing a really simple game - you have to press a key when you see green, and not press a key when you see red.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been promising for a while that there will be a red square as well as a green circle, and this time we&#x27;re going to make that dream a reality.&lt;&#x2F;p&gt;
&lt;p&gt;The code we&#x27;ve written so far has this overall structure:&lt;&#x2F;p&gt;
&lt;pre&gt;start()

ready_screen()

wait()

shape()

end()
&lt;&#x2F;pre&gt;
&lt;p&gt;It gets started, tells you to get ready, waits a random time, shows you a shape and collects your keypresses (or not), and then it ends.&lt;&#x2F;p&gt;
&lt;p&gt;Previously, the shape function just showed a green shape every time, by calling a function called green_shape. Not any more - change it to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape():
    GREEN = 0
    RED   = 1
    shape = random.choice( [GREEN, RED] )

    if shape == GREEN:
        green_shape()
    else:
        red_shape()
&lt;&#x2F;pre&gt;
&lt;p&gt;The first 2 lines just make two variables for us called GREEN and RED. They can have any values, so long as they&#x27;re not the same, so I&#x27;ve chosen 0 and 1.&lt;&#x2F;p&gt;
&lt;p&gt;The reason we&#x27;ve made these variables is so that we can make a random choice of one or the other. To do this, we call the choice function from the random module (which we already have listed as imported at the top). choice takes in a list of things to choose from, and returns the one it chose randomly.&lt;&#x2F;p&gt;
&lt;p&gt;So the shape variable will contain the value of either GREEN or RED. We do an if to decide what to do based on which it is.&lt;&#x2F;p&gt;
&lt;p&gt;If we chose GREEN, we do what we used to do, and call green_shape but if we chose RED we will end up in the else part of the if, and call a new function we will call red_shape.&lt;&#x2F;p&gt;
&lt;p&gt;So, what will red_shape look like? Quite a lot like green_shape actually. Just above the shape function, add this:&lt;&#x2F;p&gt;
&lt;pre&gt;def red_shape():
    red = pygame.Color( &quot;red&quot; )
    height = 2 * ( screen.get_height() &#x2F; 3 )
    left = ( screen.get_width() &#x2F; 2 ) - ( height &#x2F; 2 )
    top = screen.get_height() &#x2F; 6

    screen.fill( pygame.Color( &quot;white&quot; ) )
    pygame.draw.rect( screen, red, ( left, top, height, height ), 0 )

    write_text( screen, &quot;Don&#x27;t press!&quot;, pygame.Color( &quot;black&quot; ), False )

    pygame.display.flip()

    pressed = shape_wait()

    if pressed:
        red_failure()
    else:
        red_success()
&lt;&#x2F;pre&gt;
&lt;p&gt;Most of this function is taken up with drawing a red rectangle, which we do by working out what size it should be, then calling pygame.draw.rect with the right dimensions and colour. After that we write some text encouraging the player to leave their keyboard alone, and then we do the normal pygame.display.flip to show this on the screen.&lt;&#x2F;p&gt;
&lt;p&gt;Once we&#x27;ve drawn the shape, we do something very similar to what we did inside green_shape - we wait to see what happens, by calling the already-existing shape_wait function, and get the answer back from it saying whether or not the player pressed something.&lt;&#x2F;p&gt;
&lt;p&gt;This time, if they pressed something they got it wrong, so we call a new function called red_failure, and if they did nothing they did the right thing, so we call another new function called red_success.&lt;&#x2F;p&gt;
&lt;p&gt;These two functions are also quite simple. Type them in above green_shape:&lt;&#x2F;p&gt;
&lt;pre&gt;def red_success():
    tick()
    green = pygame.Color( &quot;green&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Well done!&quot;, green, True )
    write_text( screen, &quot;You didn&#x27;t press on red!&quot;, white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!

def red_failure():
    cross()
    red   = pygame.Color( &quot;red&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Bad Luck!&quot;, red, True )
    write_text( screen, &quot;Red means don&#x27;t press anything!&quot;, white, False )
    pygame.display.flip()
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!
&lt;&#x2F;pre&gt;
&lt;p&gt;These functions re-use lots of existing code - they draw a tick or a cross and then use write_text to tell the player what happened, and then they do the normal flip and wait for a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Try your program - it should now show you a red square about half of the time, instead of a green circle every time, and it should give you feedback about whether you did the right or the wrong thing. Feel free to try it a few times, and make sure you&#x27;ve run through all the combinations. If you made a mistake somewhere you may not see it until you actually run the relevant bit of code.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;re happy with that, let&#x27;s fix a little bug while we&#x27;re here. Somehow I missed a bit from the shape_wait function, so if you press a key on the ready screen, it will register as you pressing the key really quickly when the shape appears. Try running your program and hammering a key when it says &quot;Ready?&quot;. You&#x27;ll see it thinks you pressed immediately the shape appears (whether red or green). This is annoying, and could even allow cheating, but we can prevent it by adding a single line to shape_wait:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape_wait():
    &quot;&quot;&quot;
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    &quot;&quot;&quot;

    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN

    time_to_wait = 2000 # Display the shape for 2 seconds
    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )

    &lt;span style=&quot;color: green;&quot;&gt;pygame.event.clear()&lt;&#x2F;span&gt;

    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed
&lt;&#x2F;pre&gt;
&lt;p&gt;As we&#x27;ve seen before, pygame.event.clear tells PyGame to forget all the events that have happened recently, which prevents this problem.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to check your version against mine, you can find it here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-10&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve nearly built a fully-working game. We&#x27;ve got two main tasks ahead of us: fix the &quot;can&#x27;t exit&quot; bug, and allow multiple rounds with a score at the end. We&#x27;ll do them in that order, so next time it&#x27;s bug-fixing, and some more refactoring to help us do it.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    lemuel akinsete
    &lt;span class=&quot;comment_date&quot;&gt;2018-04-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;how do you make music and videos on raspberry pi&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Axel
    &lt;span class=&quot;comment_date&quot;&gt;2014-06-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Could you post the final program at the end.  I am having trouble finding out what I did wrong (whenever I try running this program it says,&lt;br&#x2F;&gt;
&quot;.&#x2F;redgreen.py&lt;br&#x2F;&gt;
File&lt;em&gt;.&#x2F;redgreen.py&lt;&#x2F;em&gt;. line 127&lt;br&#x2F;&gt;
write_text( screen, &quot;Don’t&quot;t Press!&quot;, pygame.Color( &quot;black&quot; ), False )&lt;&#x2F;p&gt;
&lt;p&gt;SyntaxError:  invalid syntax&quot;&lt;&#x2F;p&gt;
&lt;p&gt;but I can’t find out what is wrong with my script line.  That line looks the same as the one you wrote.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Axel
    &lt;span class=&quot;comment_date&quot;&gt;2014-06-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;The file says there is a problem with the word Text&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-06-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Axel, the full program listing at the end of the last video looks like: &lt;a href=&quot;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;redgreen&#x2F;master&#x2F;redgreen.py&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;andybalaam&#x2F;...&lt;&#x2F;a&gt;.  There is a link &quot;redgreen.py&quot; near the bottom of the blog post that shows what it should look like after this video.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-06-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Axel, looking at your error message, it might be that you have an extra quote after the t of Don’t.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 09 &quot; Lots more words</title>
          <pubDate>Sat, 08 Dec 2012 08:48:12 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/12/08/my-first-raspberry-pi-game-part-09-lots-more-words/</link>
          <guid>https://artificialworlds.net/blog/2012/12/08/my-first-raspberry-pi-game-part-09-lots-more-words/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/12/08/my-first-raspberry-pi-game-part-09-lots-more-words/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, 9, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 09 -- Lots more words&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a6356fc0-cedc-4697-827d-a7bfad49c91d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;re writing a really simple game - you have to press a key when you see green.&lt;&#x2F;p&gt;
&lt;p&gt;This time we&#x27;re going to add lots of instructions on each page so the player knows what to do.&lt;&#x2F;p&gt;
&lt;p&gt;Up until now, we&#x27;ve only had one piece of writing - the word &quot;Ready?&quot; when the game starts up. To make the game easy to use we want to write instructions on each page. We&#x27;ll take the code we wrote to say &quot;Ready?&quot; and turn it into a function that we can re-use for lots of different writing.&lt;&#x2F;p&gt;
&lt;p&gt;The first step we&#x27;re going to do will be &quot;refactoring&quot;, which just means changing our program without changing what it does.&lt;&#x2F;p&gt;
&lt;p&gt;Have a look at this code, that we&#x27;ve already got:&lt;&#x2F;p&gt;
&lt;pre&gt;def start():
    global screen, ready_text
    pygame.init()
    screen = pygame.display.set_mode( screen_size )
    &lt;span style=&quot;color:blue;&quot;&gt;font = pygame.font.Font( None, screen_height &#x2F; 5 )
    ready_text = font.render( &quot;Ready?&quot;, 1, pygame.Color( &quot;white&quot; ) )&lt;&#x2F;span&gt;

def quit():
    pygame.quit()
    sys.exit()

def ready_screen():
    &lt;span style=&quot;color:blue;&quot;&gt;textpos = ready_text.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = screen.get_height() &#x2F; 2
    )

    screen.blit( ready_text, textpos )&lt;&#x2F;span&gt;
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;The blue code here works together to write something on the screen. The top part inside start creates an image called ready_text, and the bottom part in ready_screen works out where to draw it, then draws it.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to extract these two bits out from where they are, and put them into a function we can re-use whenever we want to write something.&lt;&#x2F;p&gt;
&lt;p&gt;Make a function above start and call it write_text. Cut the lines in blue, and paste them into write_text. Rename the variable ready_text to rend (in three places) because it&#x27;s a rendered image of our writing. You should end up with something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;span style=&quot;color: green;&quot;&gt;def write_text():
    font = pygame.font.Font( None, screen_height &#x2F; 5 )
    rend = font.render( &quot;Ready?&quot;, 1, pygame.Color( &quot;white&quot; ) )
    textpos = rend.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = screen.get_height() &#x2F; 2
    )
    screen.blit( rend, textpos )&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Now make the code work almost exactly as before by making a call to this function from within ready_screen:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen():
    &lt;span style=&quot;color: green;&quot;&gt;write_text()&lt;&#x2F;span&gt;
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;You can try your program now and you should find it works exactly as it did before.&lt;&#x2F;p&gt;
&lt;p&gt;So far we haven&#x27;t make a very useful function, because it always writes &quot;Ready?&quot;. We can change that by adding arguments to the write_text function for the screen to write on, the text to write, and the colour of the writing. Now write_text looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def write_text( &lt;span style=&quot;color: green;&quot;&gt;screen, text, color&lt;&#x2F;span&gt; ):
    font = pygame.font.Font( None, screen.get_height() &#x2F; 5 )
    rend = font.render( &lt;span style=&quot;color: green;&quot;&gt;text&lt;&#x2F;span&gt;, 1, &lt;span style=&quot;color: green;&quot;&gt;color&lt;&#x2F;span&gt; )
    pos = rend.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = screen.get_height() &#x2F; 2
    )
    screen.blit( rend, pos )
&lt;&#x2F;pre&gt;
&lt;p&gt;and ready_screen looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen():
    &lt;span style=&quot;color: green;&quot;&gt;white = pygame.Color( &quot;white&quot; )&lt;&#x2F;span&gt;
    write_text( &lt;span style=&quot;color: green;&quot;&gt;screen, &quot;Ready?&quot;, white&lt;&#x2F;span&gt; )
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;(Notice that I made a variable called white and then passed it in.)&lt;&#x2F;p&gt;
&lt;p&gt;At this point, you can also delete the line above write_text that says ready_text = None because we don&#x27;t need that variable any more (we are just using the variable rend inside write_text) and you can remove ready_text from the first line of the start function, so it just reads global screen.&lt;&#x2F;p&gt;
&lt;p&gt;Again, you can try your program now and you should find it works exactly as it did before.&lt;&#x2F;p&gt;
&lt;p&gt;By working in gradual steps like this, we give ourselves small enough chunks of things to think about that we&#x27;re relatively unlikely to get confused and screw things up. As you work with longer programs I hope you&#x27;ll see how useful this technique can be.&lt;&#x2F;p&gt;
&lt;p&gt;The next step we want to take is to allow two different types of writing: either in the middle, big or at the bottom, smaller. Let&#x27;s add another argument, called big and make it control how big the writing is, and where it goes.&lt;&#x2F;p&gt;
&lt;pre&gt;def write_text( screen, text, color, &lt;span style=&quot;color: green;&quot;&gt;big&lt;&#x2F;span&gt; ):
    &lt;span style=&quot;color: green;&quot;&gt;if big:
        height = screen.get_height() &#x2F; 5
        up = screen.get_height() &#x2F; 2&lt;&#x2F;span&gt;
    font = pygame.font.Font( None, &lt;span style=&quot;color: green;&quot;&gt;height&lt;&#x2F;span&gt; )
    rend = font.render( text, 1, color )
    pos = rend.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = &lt;span style=&quot;color: green;&quot;&gt;up&lt;&#x2F;span&gt;
    )
    screen.blit( rend, up )
&lt;&#x2F;pre&gt;
&lt;p&gt;And let&#x27;s pass in True so we behave just as we did before:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen():
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Ready?&quot;, white, &lt;span style=&quot;color: green;&quot;&gt;True&lt;&#x2F;span&gt; )
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;So far we&#x27;ve only said what happens when we pass True for big - if we pass False, height and pos will not be set, and the program will go wrong. Let&#x27;s fix that now:&lt;&#x2F;p&gt;
&lt;pre&gt;def write_text( screen, text, color, &lt;span style=&quot;color: green;&quot;&gt;big&lt;&#x2F;span&gt; ):
    if big:
        height = screen.get_height() &#x2F; 5
        up = screen.get_height() &#x2F; 2
    &lt;span style=&quot;color: green;&quot;&gt;else:
        height = screen_height &#x2F; 12
        up = screen.get_height() - ( screen_height &#x2F; 24 )&lt;&#x2F;span&gt;
    font = pygame.font.Font( None, height )
    rend = font.render( text, 1, color )
    pos = rend.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = up
    )
    screen.blit( rend, pos )
&lt;&#x2F;pre&gt;
&lt;p&gt;So now we can write small text at the bottom of the sreen. Let&#x27;s do that on the green_shape page, so the player knows to press a key. Change green_shape to look like:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_shape():
    green = pygame.Color( &quot;green&quot; )
    centre = ( screen.get_width() &#x2F; 2, screen.get_height() &#x2F; 2 )
    radius = screen.get_width() &#x2F; 3

    screen.fill( pygame.Color( &quot;white&quot; ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    &lt;span style=&quot;color: green;&quot;&gt;write_text( screen, &quot;Press something!&quot;, pygame.Color( &quot;black&quot; ), False )&lt;&#x2F;span&gt;

    pygame.display.flip()

    pressed = shape_wait()

    if pressed:
        green_success()
    else:
        green_failure()
&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can add some more text to green_success and green_failure saying well done when you won and bad luck when you lost:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_success():
    tick()
    &lt;span style=&quot;color: green;&quot;&gt;green = pygame.Color( &quot;green&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Well done!&quot;, green, True )
    write_text( screen, &quot;You pressed on green!&quot;, white, False )
    pygame.display.flip()&lt;&#x2F;span&gt;
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!

def green_failure():
    cross()
    &lt;span style=&quot;color: green;&quot;&gt;red   = pygame.Color( &quot;red&quot; )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Bad Luck!&quot;, red, True )
    write_text( screen, &quot;Green means press something!&quot;, white, False )
    pygame.display.flip()&lt;&#x2F;span&gt;
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that we have added pygame.display.flip() to the end of each of these sections, so we can remove that line from the end of tick and cross. Also, we need to move our tick and cross pictures up so they&#x27;re not on top of the writing, so we&#x27;ll divide by 4 instead of 2 to get the middle of the picture:&lt;&#x2F;p&gt;
&lt;pre&gt;def tick():
    colour = pygame.Color( &quot;green&quot; )
    w = screen.get_width() &#x2F; 2
    h = &lt;span style=&quot;color: green&quot;&gt;screen.get_height() &#x2F; 4&lt;&#x2F;span&gt;
    points = (
        ( w - w&#x2F;5, h - h&#x2F;9 ),
        ( w,       h + h&#x2F;5 ),
        ( w + w&#x2F;3, h - h&#x2F;3 ),
    )

    screen.fill( pygame.Color( &quot;black&quot; ) )
    pygame.draw.lines( screen, colour, False, points, 20 )
    &lt;span style=&quot;color: red; text-decoration: line-through;&quot;&gt;pygame.display.flip()&lt;&#x2F;span&gt;

def cross():
    colour = pygame.Color( &quot;red&quot; )
    w = screen.get_width() &#x2F; 2
    h = &lt;span style=&quot;color: green&quot;&gt;screen.get_height() &#x2F; 4&lt;&#x2F;span&gt;
    left   = w - w&#x2F;3
    right  = w + w&#x2F;3
    top    = h - h&#x2F;3
    bottom = h + h&#x2F;3

    start1 = left, top
    end1   = right, bottom

    start2 = left, bottom
    end2   = right, top

    screen.fill( pygame.Color( &quot;black&quot; ) )
    pygame.draw.line( screen, colour, start1, end1, 20 )
    pygame.draw.line( screen, colour, start2, end2, 20 )
    &lt;span style=&quot;color: red; text-decoration: line-through;&quot;&gt;pygame.display.flip()&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;And finally, let&#x27;s say goodbye at the end. Change end to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def end():
    &lt;span style=&quot;color: green;&quot;&gt;screen.fill( pygame.Color( &quot;black&quot; ) )
    white = pygame.Color( &quot;white&quot; )
    write_text( screen, &quot;Thanks for playing!&quot;, white, True )
    write_text( screen, &quot;Press a key to exit&quot;, white, False )
    pygame.display.flip()&lt;&#x2F;span&gt;

    pygame.event.clear()
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
&lt;&#x2F;pre&gt;
&lt;p&gt;Now we&#x27;ve got much more enlightened players of our game!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve made lots of changes in lots of different places today. If something doesn&#x27;t work, check your version again mine: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-09&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next time we&#x27;ll show red squares as well as green circles, making the game significantly more interesting.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 08 &quot; Success and failure</title>
          <pubDate>Tue, 04 Dec 2012 08:29:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/12/04/my-first-raspberry-pi-game-part-08-success-and-failure/</link>
          <guid>https://artificialworlds.net/blog/2012/12/04/my-first-raspberry-pi-game-part-08-success-and-failure/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/12/04/my-first-raspberry-pi-game-part-08-success-and-failure/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, 8, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 08 -- Success and Failure&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c7f1db47-40ff-4cf9-9ef5-328feb41ca6c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;re writing a really simple game - you have to press a key when you see green.&lt;&#x2F;p&gt;
&lt;p&gt;Today we&#x27;re going to wait for a key press. If we get one, we&#x27;ll tell the player they did well. If not, we&#x27;ll tell them they are a bad person.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to change the green_shape function first, to make it wait for a key press (or give up waiting) and then tell the player what happened.&lt;&#x2F;p&gt;
&lt;p&gt;Find the green_shape function and add the new bit that I&#x27;ve highlighted in green, at the end:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_shape():
    green = pygame.Color( &quot;green&quot; )
    centre = ( screen.get_width() &#x2F; 2, screen.get_height() &#x2F; 2 )
    radius = screen.get_width() &#x2F; 3

    screen.fill( pygame.Color( &quot;white&quot; ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    pygame.display.flip()

    &lt;span style=&quot;color: green;&quot;&gt;pressed = shape_wait()

    if pressed:
        green_success()
    else:
        green_failure()&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;green_shape is the function that shows a green shape to the player.&lt;&#x2F;p&gt;
&lt;p&gt;This new code does 2 things. First, it calls a function shape_wait (that we haven&#x27;t written yet) that waits for a key press. We are expecting this function to give us back an answer, which we will store inside a new variable, pressed.&lt;&#x2F;p&gt;
&lt;p&gt;Second, it checks the value of pressed, and calls a different function in each case. If a key was pressed, this is good (because we&#x27;re showing a green shape, so you&#x27;re supposed to press a key) so we call the green_success function (which we haven&#x27;t written yet either). If no key was pressed because we gave up waiting, we call the green_failure function (which we haven&#x27;t written yet!).&lt;&#x2F;p&gt;
&lt;p&gt;That covers everything we want to do today - all we have to do is write those 3 missing functions.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s start with the hardest one - shape_wait. Go up to just above the green_shape function, and type this:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape_wait():
    &quot;&quot;&quot;
    Wait while we display a shape.  Return True if a key was pressed,
    or false otherwise.
    &quot;&quot;&quot;

    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN

    time_to_wait = 2000 # Display the shape for 2 seconds
    finished_waiting_event_id = pygame.USEREVENT + 1
    pygame.time.set_timer( finished_waiting_event_id, time_to_wait )
&lt;&#x2F;pre&gt;
&lt;p&gt;There are a few things to explain here. First, the writing at the top just below the def line. This is the way we explain in Python what a function does and what it&#x27;s for. It&#x27;s optional, and we haven&#x27;t done it before, but I thought this function was interesting enough for us to provide some explanation. Notice the triple-quotes &quot;&quot;&quot; at the beginning and end. That is a way Python allows us to write longer strings of text that cover more than one line. The string starts at the first triple-quote, and ends at the last.&lt;&#x2F;p&gt;
&lt;p&gt;After our documentation string, we create a familiar variable event_types_that_cancel that holds on to all the types of event we are interested in - key presses and mouse clicks. Next we remember how long we are going to wait before giving up in another variable time_to_wait.&lt;&#x2F;p&gt;
&lt;p&gt;After that we do something a bit interesting. Up to now we have been dealing with &quot;events&quot; - things that happen such as mouse clicks, key presses and mouse movements, but we have only been responding to them, not creating them. The next 2 lines are how we create our own event, that we want to respond to later.&lt;&#x2F;p&gt;
&lt;p&gt;What we want to do is make an event happen in 2 seconds&#x27; time, so that we can give up waiting when it comes. The way we do that is first create an &quot;ID&quot; for it. This is just a numeric &quot;name&quot; that we can use to talk about the same type of event later. In PyGame the right ID to choose for an event you created yourself is pygame.USEREVENT + 1 (and higher numbers if you need more than one). We don&#x27;t know what number PyGame has stored inside its own variable pygame.USEREVENT, and we don&#x27;t care - all we care about is that PyGame says if we use numbers bigger than that, we&#x27;ll be fine. If we use smaller numbers, we are going to clash with the built-in events like pygame.KEYDOWN that we have already seen.&lt;&#x2F;p&gt;
&lt;p&gt;Once we have an appropriate ID stored inside finished_waiting_event_id we are ready to ask PyGame to create an event that will happen in 2 seconds&#x27; time. We do that by calling the set_timer function inside pygame.time.&lt;&#x2F;p&gt;
&lt;p&gt;Now continue the function by typing all this:&lt;&#x2F;p&gt;
&lt;pre&gt;    pressed = False
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
            pressed = True
        elif evt.type == finished_waiting_event_id:
            waiting = False

    pygame.time.set_timer( finished_waiting_event_id, 0 )

    return pressed
&lt;&#x2F;pre&gt;
&lt;p&gt;This is the code that waits for something to happen. It&#x27;s quite similar to the loop we saw in &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;part 6&lt;&#x2F;a&gt;, where we were also waiting for something to happen, but it&#x27;s slightly more complicated because we have to handle more possibilities.&lt;&#x2F;p&gt;
&lt;p&gt;This function will provide an answer to the code that called it, and the answer is going to be whether or not the player pressed a key. Providing an answer like this is called &quot;returning a value&quot; and we do it by writing a line like the last one here, using the return statement. The first line above creates a variable called pressed, which starts off set to False, meaning they haven&#x27;t pressed anything, and somewhere in between it might get set to True, and then the last line returns this answer - True or False for whether or not a key was pressed.&lt;&#x2F;p&gt;
&lt;p&gt;In between we have a loop similar to part 6 - we create a variable called waiting which tells us whether to keep looping, and then we loop using the while line through all the lines indented below it. The inside of the loop (the part that gets repeated) waits for an event to happen with pygame.event.wait and then has a series of if and elif sections, that do different things depending what type of event happened.&lt;&#x2F;p&gt;
&lt;p&gt;First (the if part), we check whether the player closed the window. If so, we call our function quit, that stops everything immediately.&lt;&#x2F;p&gt;
&lt;p&gt;Next (the first elif), we check whether a key or mouse button was pressed. If so, we make sure the value we will return inside pressed is updated to say a key was pressed (i.e. we make it True), and then we set waiting to False so that we will stop looping at the end of this repeat.&lt;&#x2F;p&gt;
&lt;p&gt;Now (the second elif), we check whether what happened was the special event we created earlier when we called set_timer. If so, we need to end the loop (so we set waiting to False), but no key was pressed, so we leave pressed as it was.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, if the event that happened didn&#x27;t fit any of our categories (for example it might have been a mouse movement event), we do absolutely nothing because none of the if or elif sections was triggered. We jump straight back to the start of the loop and start waiting for the next event to happen.&lt;&#x2F;p&gt;
&lt;p&gt;So, eventually, either an interesting event happens, or the &quot;we&#x27;ve been waiting too long&quot; event we created happens, and we come out of the while loop. The last thing we have to do is cancel the &quot;we&#x27;ve been waiting too long&quot; event, just in case it hasn&#x27;t happened yet - we don&#x27;t want it confusing us later. We do that by calling set_timer again, with the same ID as before, but with 0 for the amount of time to wait - this tells PyGame we&#x27;re not interested in that event any more.&lt;&#x2F;p&gt;
&lt;p&gt;Once we&#x27;ve done that we return the answer about whether a key was pressed, and we&#x27;re done with shape_wait.&lt;&#x2F;p&gt;
&lt;p&gt;Next up are green_success and green_failure. These tell the player whether they succeeded or failed - did they manage to press when they saw green?&lt;&#x2F;p&gt;
&lt;p&gt;They&#x27;re both quite simple. Type these just above green_shape:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_success():
    tick()
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!

def green_failure():
    cross()
    pygame.time.wait( 2000 ) # Can&#x27;t quit or skip!
&lt;&#x2F;pre&gt;
&lt;p&gt;If a key was pressed on green, we want to draw a &quot;tick&quot; mark on the screen, so we call a function tick that we&#x27;ll write in a moment. Similarly, if a key wasn&#x27;t pressed, we will draw a cross.&lt;&#x2F;p&gt;
&lt;p&gt;Drawing shapes is fairly straightforward, but a bit verbose. Just above green_success type these 2 functions:&lt;&#x2F;p&gt;
&lt;pre&gt;def tick():
    colour = pygame.Color( &quot;green&quot; )
    w = screen.get_width() &#x2F; 2
    h = screen.get_height() &#x2F; 2
    points = (
        ( w - w&#x2F;5, h - h&#x2F;9 ),
        ( w,       h + h&#x2F;5 ),
        ( w + w&#x2F;3, h - h&#x2F;3 ),
    )

    screen.fill( pygame.Color( &quot;black&quot; ) )
    pygame.draw.lines( screen, colour, False, points, 20 )
    pygame.display.flip()

def cross():
    colour = pygame.Color( &quot;red&quot; )
    w = screen.get_width() &#x2F; 2
    h = screen.get_height() &#x2F; 2
    left   = w - w&#x2F;3
    right  = w + w&#x2F;3
    top    = h - h&#x2F;3
    bottom = h + h&#x2F;3

    start1 = left, top
    end1   = right, bottom

    start2 = left, bottom
    end2   = right, top

    screen.fill( pygame.Color( &quot;black&quot; ) )
    pygame.draw.line( screen, colour, start1, end1, 20 )
    pygame.draw.line( screen, colour, start2, end2, 20 )
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;Both of these functions get some variables ready, do some maths on them to decide where on the screen to start and end the lines they are drawing, and then draw the lines (after making a black background with screen.fill).&lt;&#x2F;p&gt;
&lt;p&gt;The tick is drawn by passing in a list of 3 points on the screen to the pygame.draw.lines function, and the cross is drawn using two separate calls to pygame.draw.line, one for each line. After we&#x27;ve drawn our lines, we call pygame.display.flip as normal to show them on the screen.&lt;&#x2F;p&gt;
&lt;p&gt;With those two functions in place, we&#x27;re ready to try it out. Open LXTerminal in the usual way, and type our usual incantation:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;If all has gone well, you should see the green shape as before, but when you press a key a tick should appear. If you don&#x27;t press a key, after a while a red cross should appear.&lt;&#x2F;p&gt;
&lt;p&gt;If that doesn&#x27;t happen, check your typing really carefully, and compare your version with mine: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-08&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll add some writing explaining what you should do at each step.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 07 &quot; A green circle</title>
          <pubDate>Wed, 28 Nov 2012 09:34:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/28/my-first-raspberry-pi-game-part-07-a-green-circle/</link>
          <guid>https://artificialworlds.net/blog/2012/11/28/my-first-raspberry-pi-game-part-07-a-green-circle/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/28/my-first-raspberry-pi-game-part-07-a-green-circle/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, 7, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 07 -- A green circle&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;6614e61d-1579-42f6-916a-ad7e00bf2dad&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;re going to write a game that tests your reactions - press a key when you see green, but don&#x27;t when you see red.&lt;&#x2F;p&gt;
&lt;p&gt;Today we see some of what we have been &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;waiting for&lt;&#x2F;a&gt; - a genuine bona-fide green circle, made by you!&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to need some random numbers, so edit your program in LeafPad, and add a line, just before import pygame near the top:&lt;&#x2F;p&gt;
&lt;pre&gt;import random
&lt;&#x2F;pre&gt;
&lt;p&gt;This makes the &quot;random&quot; module available to us so we can make some numbers later.&lt;&#x2F;p&gt;
&lt;p&gt;Remember we had a function called &quot;wait&quot;, but it never did anything? It was supposed to wait for a random amount of time before we showed our green or red shape. Let&#x27;s write it now. Find the empty wait function and replace it with:&lt;&#x2F;p&gt;
&lt;pre&gt;def wait():
    time_to_wait = random.randint( 1500, 3000 ) # Between 1.5 and 3 seconds
    pygame.time.wait( time_to_wait ) # Note bug: can&#x27;t quit during this time
&lt;&#x2F;pre&gt;
&lt;p&gt;The first line makes a variable time_to_wait and puts a random number into it. The random.randint function gives us a random number between the two numbers we supplied, so here between 1,500 and 3,000. time_to_wait is a time in milliseconds, so this means between one and half and three seconds.&lt;&#x2F;p&gt;
&lt;p&gt;After the closing bracket, we have a hash symbol #, and then some writing. This is a &quot;comment&quot;, and it is completely ignored by Python. It&#x27;s just for us.&lt;&#x2F;p&gt;
&lt;p&gt;[As time goes on, I hope you will begin to see programming more and more as talking to other people, not just to the computer. It&#x27;s fairly easy to write a computer program, but much harder to understand one written by someone else. Most programs live a long time, and people need to understand them to keep them up-to-date, so making them as easy to understand as possible is very important. Comments are one way to help people understand, but in a way they are a last resort - if possible, the code itself should be so easy to understand that you don&#x27;t need many comments. Here, I thought that the translation between seconds and milliseconds might be helpful to someone looking at this later.]&lt;&#x2F;p&gt;
&lt;p&gt;The next line uses a function inside PyGame&#x27;s time module to wait for the amount of time we give it (in milliseconds, stored in time_to_wait). Note that this is not the same wait function we have seen before, pygame.event.wait. That one waits forever for an event to happen, but this one waits (and can&#x27;t be interrupted) for the amount of time we say.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve added another comment to this line saying that there&#x27;s a bug in our program: if we write it like this, you can&#x27;t actually quit the game by closing the window while we&#x27;re waiting. The pygame.time.wait function won&#x27;t be interrupted by the window being closed, so we&#x27;ll ignore it. This is almost unbearably rude, but don&#x27;t worry - we&#x27;ll fix it soon (ish).&lt;&#x2F;p&gt;
&lt;p&gt;And now for the really exciting part: we&#x27;re going to draw a green shape on the screen. Let&#x27;s make a function, just above the shape function, called green_shape:&lt;&#x2F;p&gt;
&lt;pre&gt;def green_shape():
    green = pygame.Color( &quot;green&quot; )
    centre = ( screen.get_width() &#x2F; 2, screen.get_height() &#x2F; 2 )
    radius = screen.get_width() &#x2F; 3

    screen.fill( pygame.Color( &quot;white&quot; ) )
    pygame.draw.circle( screen, green, centre, radius, 0 )

    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;This code makes a variable green holding onto the colour green, one called centre holding the co-ordinates of the centre of the screen, and one called radius holding the size of the circle we want to draw.&lt;&#x2F;p&gt;
&lt;p&gt;Then it uses the fill function on screen to colour in the screen white, and then draws our circle with a call to pygame.draw.circle, using the variables we have prepared as arguments, telling it where to draw the circle, in what colour, and what size.&lt;&#x2F;p&gt;
&lt;p&gt;Finally it uses flip as before to tell PyGame we have finished.&lt;&#x2F;p&gt;
&lt;p&gt;The last piece of today&#x27;s jigsaw is just to call the function we created above. Find the empty shape function, and make it look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def shape():
    green_shape()
&lt;&#x2F;pre&gt;
&lt;p&gt;This literally just means run the green_shape function.&lt;&#x2F;p&gt;
&lt;p&gt;Take a deep breath, prepare to be excited, open LXTerminal and run our new program in the usual way:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;If all has gone well, the ready screen will appear for a couple of seconds, before a white screen with a big green circle on it appears. This will then go away when you press a key.&lt;&#x2F;p&gt;
&lt;p&gt;If something goes wrong, check back what you typed, and compare your version against mine: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-07&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll find out whether you pressed a key or were too slow!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Launching an OSGi app on the command line</title>
          <pubDate>Tue, 27 Nov 2012 10:29:54 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/27/launching-an-osgi-app-on-the-command-line/</link>
          <guid>https://artificialworlds.net/blog/2012/11/27/launching-an-osgi-app-on-the-command-line/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/27/launching-an-osgi-app-on-the-command-line/">&lt;p&gt;I am a total beginner on this topic, but here is what I have found so far. Please correct me, advise me etc. in the comments.&lt;&#x2F;p&gt;
&lt;p&gt;A colleague has written a program based on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.osgi.org&quot;&gt;OSGi&lt;&#x2F;a&gt;, which is a framework for &lt;del&gt;obfuscating everything&lt;&#x2F;del&gt; &quot;modular&quot; software, which I think means loading code at runtime, even if it requires a different classloader, and even if it depends on other things which also need loading at runtime. I may be wrong though.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, all I want to do is run it (on windows only, for now).&lt;&#x2F;p&gt;
&lt;p&gt;OSGi is quite closely linked to the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.eclipse.org&#x2F;&quot;&gt;Eclipse&lt;&#x2F;a&gt; development environment - Eclipse uses OSGi to manage its plugins, which are an infinitely complex web of incompatible interdependencies.&lt;&#x2F;p&gt;
&lt;p&gt;My colleague showed me how to set up their Eclipse environment, and how to run the program from within Eclipse using an OSGi launch configuration. I also managed to build the JAR files of the program itself.&lt;&#x2F;p&gt;
&lt;p&gt;What I wanted to do was run it outside Eclipse, on the command line (and eventually in an &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ant.apache.org&#x2F;&quot;&gt;Ant&lt;&#x2F;a&gt; build).&lt;&#x2F;p&gt;
&lt;p&gt;I found it almost impossible to understand anything others have written about OSGi, but somehow I muddled my way through to being able to do it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I managed it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;find-out-the-dependencies&quot;&gt;Find out the dependencies&lt;&#x2F;h3&gt;
&lt;p&gt;If you have an Eclipse launch configuration that successfully runs your program inside Eclipse, you can find out all the OSGi bundles (which are JAR files containing extra config) that it needs. Go to the Run menu and click Run Configurations. Find the relevant run configuration, which will be inside the OSGi Framework tree item on the left. Click on it, and choose the Bundles tab. The tree view will show you all the bundles needed to run this program. If you check &quot;Only show selected&quot; you can see only the ones you need.&lt;&#x2F;p&gt;
&lt;p&gt;If you want a text version of this list, look for a file here:&lt;&#x2F;p&gt;
&lt;pre&gt;WORKSPACE&#x2F;.metadata&#x2F;.plugins&#x2F;org.eclipse.pde.core&#x2F;LAUNCHERNAME&#x2F;org.eclipse.simpleconfigurator&#x2F;bundles.info&lt;&#x2F;pre&gt;
&lt;p&gt;where WORKSPACE is the location of your Eclipse workspace, and LAUNCHERNAME is the name of the run configuration you found in the tree view in the Run Configurations page.&lt;&#x2F;p&gt;
&lt;p&gt;The bundles.info file contains a comma-separated list of bundle name (which is a reversed-url type name, like a package name - don&#x27;t confuse it with a package name though), some version information, and the path to the actual JAR file that is this bundle.&lt;&#x2F;p&gt;
&lt;p&gt;We will also need the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.eclipse.org&#x2F;equinox&#x2F;&quot;&gt;Equinox&lt;&#x2F;a&gt; OSGi bundles, including the SimpleConfigurator (oh, the irony). I am using these versions - yours might be different:&lt;&#x2F;p&gt;
&lt;pre&gt;org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222&#x2F;*
org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar
org.eclipse.equinox.common_3.6.0.v20100503.jar
org.eclipse.equinox.concurrent_1.0.100.v20100503.jar
org.eclipse.equinox.ds_1.2.1.R36x_v20100803.jar
org.eclipse.equinox.event_1.2.0.v20100503.jar
org.eclipse.equinox.frameworkadmin.equinox_1.0.200.v20100505.jar
org.eclipse.equinox.frameworkadmin_2.0.0.v20100503.jar
org.eclipse.equinox.http.jetty_2.0.0.v20100503.jar
org.eclipse.equinox.http.registry_1.1.1.R36x_v20101103.jar
org.eclipse.equinox.http.servlet_1.1.0.v20100503.jar
org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar
org.eclipse.equinox.p2.artifact.repository_1.1.1.R36x_v20100901.jar
org.eclipse.equinox.p2.console_1.0.200.v20100601.jar
org.eclipse.equinox.p2.core_2.0.3.R36x_v20110111.jar
org.eclipse.equinox.p2.director.app_1.0.202.R36x_v20110608.jar
org.eclipse.equinox.p2.directorywatcher_1.0.203.R36x_v20101220.jar
org.eclipse.equinox.p2.director_2.0.4.R36x_v20120223.jar
org.eclipse.equinox.p2.engine_2.0.1.R36x_v20110201.jar
org.eclipse.equinox.p2.extensionlocation_1.2.0.v20100518.jar
org.eclipse.equinox.p2.garbagecollector_1.0.100.v20100503.jar
org.eclipse.equinox.p2.jarprocessor_1.0.200.v20100503a.jar
org.eclipse.equinox.p2.metadata.repository_1.1.1.R36x_v20110302.jar
org.eclipse.equinox.p2.metadata_2.0.1.R36x_v20101202.jar
org.eclipse.equinox.p2.operations_2.0.0.v20100510.jar
org.eclipse.equinox.p2.publisher_1.1.2.v20100824-2220.jar
org.eclipse.equinox.p2.ql_2.0.0.v20100503a.jar
org.eclipse.equinox.p2.reconciler.dropins_1.1.3.R36x_v20111213.jar
org.eclipse.equinox.p2.repository.tools_2.0.1.R36x_v20100823.jar
org.eclipse.equinox.p2.repository_2.0.2.R36x_v20110111-1500.jar
org.eclipse.equinox.p2.touchpoint.eclipse_2.0.4.R36x_v20110908.jar
org.eclipse.equinox.p2.touchpoint.natives_1.0.201.R36x_v20110111.jar
org.eclipse.equinox.p2.ui.sdk.scheduler_1.0.0.v20100507-1815.jar
org.eclipse.equinox.p2.ui.sdk_1.0.100.v20100513.jar
org.eclipse.equinox.p2.ui_2.0.0.v20100518.jar
org.eclipse.equinox.p2.updatechecker_1.1.101.R36x_v20100823.jar
org.eclipse.equinox.p2.updatesite_1.0.201.R36x_v20100823.jar
org.eclipse.equinox.preferences_3.3.1.R36x_v20110302.jar
org.eclipse.equinox.registry_3.5.0.v20100503.jar
org.eclipse.equinox.security.ui_1.0.200.v20100503.jar
org.eclipse.equinox.security.win32.x86_1.0.200.v20100503.jar
org.eclipse.equinox.security_1.0.200.v20100503.jar
org.eclipse.equinox.simpleconfigurator.manipulator_2.0.0.v20100503.jar
org.eclipse.equinox.simpleconfigurator_1.0.200.v20100503.jar
org.eclipse.equinox.util_1.0.200.v20100503.jar
org.eclipse.osgi.services_3.2.100.v20100503.jar
org.eclipse.osgi.util_3.2.100.v20100503.jar
org.eclipse.osgi_3.6.50.R36x_v20120315-1500.jar
&lt;&#x2F;pre&gt;
&lt;p&gt;(I have no idea whether you need all of these - sorry.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-a-fake-environment&quot;&gt;Make a fake environment&lt;&#x2F;h3&gt;
&lt;p&gt;Imagine you have a program that consists of 2 JARs itself, called my_program_main_1.0.jar and my_program_lib_1.1.jar, 2 dependency bundles called com.dep1_3.6v20091112.jar and org.dep2_1.0.jar and a directory containing a dependency bundle called org.foo.dep3_1.4.&lt;&#x2F;p&gt;
&lt;p&gt;Create a directory that will contain your program and its dependencies. Structure it like this:&lt;&#x2F;p&gt;
&lt;pre&gt;main_dir&#x2F;
    my_program&#x2F;
        my_program_main_1.0.jar
        my_program_lib_1.1.jar
    osgi_install_area&#x2F;
        plugins&#x2F;
            com.dep1_3.6v20091112.jar
            org.dep2_1.0.jar
            org.foo.dep3_1.4&#x2F;
                ... more stuff in here ...
            org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222&#x2F;
                ... more stuff in here ...
            org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar
            ... lots more here ...
    configuration&#x2F;
        config.ini
    bundles.info
&lt;&#x2F;pre&gt;
&lt;p&gt;Copy your program&#x27;s JARs into the my_program directory (feel free to give it a different name!).&lt;&#x2F;p&gt;
&lt;p&gt;Copy the Equinox bundles and the other dependencies into the plugins directory. You can find them in the directory listed in the bundles.info file you found in the previous section. The Equinox bundles should be alongside the others (hopefully).&lt;&#x2F;p&gt;
&lt;p&gt;The contents of bundles.info should be something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;#version=1
MyProgramMain,1.0,file:..&#x2F;my_program&#x2F;my_program_main_1.0.jar,4,true
MyProgramLib,1.1,file:..&#x2F;my_program&#x2F;my_program_lib_1.1.jar,4,true
com.dep1,3.6v20091112,file:.&#x2F;plugins&#x2F;com.dep1_3.6v20091112.jar,4,true
org.dep2,1.0,file:.&#x2F;plugins&#x2F;org.dep2_1.0.jar,4,true
org.foo.dep3,1.4,file:.&#x2F;plugins&#x2F;org.foo.dep3_1.4&#x2F;,4,true
org.eclipse.equinox.launcher.win32.win32.x86,1.1.2.R36x_v20101222,file:.&#x2F;plugins&#x2F;org.eclipse.equinox.launcher.win32.win32.x86_1.1.2.R36x_v20101222&#x2F;,4,true
org.eclipse.equinox.app,1.3.1.R36x_v20100803,org.eclipse.equinox.app_1.3.1.R36x_v20100803.jar,4,true
... Plus similar lines for all the equinox and OSGi bundles you need ...
&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure that the version number after the first comma exactly matches the version number encoded in the JAR filename.&lt;&#x2F;p&gt;
&lt;p&gt;The contents of config.ini should be something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;osgi.bundles=reference\:file\:C\:&#x2F;absolute&#x2F;path&#x2F;to&#x2F;main_dir&#x2F;osgi_install_area&#x2F;plugins&#x2F;org.eclipse.equinox.simpleconfigurator_1.0.200.v20100503.jar@1\:start
org.eclipse.update.reconcile=false
osgi.bundles.defaultStartLevel=4
osgi.install.area=file\:C\:&#x2F;absolute&#x2F;path&#x2F;to&#x2F;main_dir&#x2F;osgi_install_area
osgi.framework=file\:C\:&#x2F;absolute&#x2F;path&#x2F;to&#x2F;main_dir&#x2F;osgi_install_area&#x2F;plugins&#x2F;org.eclipse.osgi_3.6.50.R36x_v20120315-1500.jar
org.eclipse.equinox.simpleconfigurator.configUrl=file\:&#x2F;C\:&#x2F;absolute&#x2F;path&#x2F;to&#x2F;main_dir&#x2F;bundles.info
osgi.configuration.cascaded=false
&lt;&#x2F;pre&gt;
&lt;p&gt;Note that these paths have to be absolute. I plan to write a small ant script to create the config.ini file when needed.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;run-the-program&quot;&gt;Run the program&lt;&#x2F;h3&gt;
&lt;p&gt;Now, to run your program, it&#x27;s a simple matter of getting into the right directory:&lt;&#x2F;p&gt;
&lt;pre&gt;cd &quot;C:\absolute\path\to\main_dir&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;and typing this simple command:&lt;&#x2F;p&gt;
&lt;pre&gt;java -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dfile.encoding=Cp1252 -classpath &quot;osgi_install_area\plugins\org.eclipse.equinox.launcher_1.1.1.R36x_v20101122_1400.jar&quot; org.eclipse.equinox.launcher.Main -configuration &quot;file:configuration&#x2F;&quot; -os win32 -ws win32 -arch x86 -nl en_GB -console ACTUAL_ARGS_TO_PROGRAM
&lt;&#x2F;pre&gt;
&lt;p&gt;Where ACTUAL_ARGS_TO_PROGRAM is where you type the actual command-line arguments (if any) you want to pass to your program.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;disclaimer&quot;&gt;Disclaimer&lt;&#x2F;h3&gt;
&lt;p&gt;I have no idea what most of this means, and I&#x27;ve probably told you a lot of things that are wrong. Please, people who know about this stuff, correct me and I&#x27;ll update this post.&lt;&#x2F;p&gt;
&lt;p&gt;This worked for me, but I feel like I must be doing something wrong - can it really be this difficult?&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Teu
    &lt;span class=&quot;comment_date&quot;&gt;2016-12-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi there, simple question (hooping you still read this old post)&lt;&#x2F;p&gt;
&lt;p&gt;Where is the point where you tell which application framwork must run? i suppose is main class in you MyProgramMain.jar ..where ACTUAL_ARGS_TO_PROGRAM goes.&lt;&#x2F;p&gt;
&lt;p&gt;Becaouse in you command line there is not reference to you MyProgramMain, in  bundles.info line with MyProgramMain.jar seems similar to others line and in config.ini i don’t see anything can help.&lt;&#x2F;p&gt;
&lt;p&gt;I try to replicate but nothing invoke my main class.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-12-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Teu, I don’t think OSGi uses Main classes or methods but to be honest I found it totally confusing and am very glad I never have to touch it again.  Good luck, and sorry I can’t help!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Neil Bartlett
    &lt;span class=&quot;comment_date&quot;&gt;2014-12-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You can run an OSGi framework with just &quot;java -jar org.eclipse.osgi_.jar&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The problems you have encountered seem related to the vast number of bundles you have added to the framework. You admit yourself that you don’t know what they do, so why are you bothering with them?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-12-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Neil, thanks for the information.  The bundles were needed by the code I was running, which I didn’t write.  The main problem I was having was constructing a directory structure containing all the right stuff in order to be able to run the program, rather than the actual command line needed to run it (although that was a challenge too).  I’d appreciate any tips on how to &quot;build&quot; something running inside Eclipse into a &quot;executable&quot; project (i.e. a file or set of files that can be run using the command you gave or a similar one).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 06 &quot; A better class of waiting</title>
          <pubDate>Sat, 24 Nov 2012 08:44:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/24/my-first-raspberry-pi-game-part-06-a-better-class-of-waiting/</link>
          <guid>https://artificialworlds.net/blog/2012/11/24/my-first-raspberry-pi-game-part-06-a-better-class-of-waiting/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/24/my-first-raspberry-pi-game-part-06-a-better-class-of-waiting/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, 6, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 06 -- A better class of waiting&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a04231e7-0cc7-43ca-be00-8fbab335b103&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Today we wait, but better, while we show the &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;ready screen&lt;&#x2F;a&gt; we made last time. In the process we cover two of the most important things you need to understand to write computer programs.&lt;&#x2F;p&gt;
&lt;p&gt;The program we&#x27;ve written so far can wait around for something to happen, but it can&#x27;t distinguish between the various possible &quot;somethings&quot; that might happen. Specifically, it finishes very abruptly if we even move our mouse over the window!&lt;&#x2F;p&gt;
&lt;p&gt;In this part we&#x27;ll teach our program to ignore the movements of the mouse, but keep looking out for events it is interested in. We&#x27;ll also make it able to exit quickly if someone closes its window.&lt;&#x2F;p&gt;
&lt;p&gt;First, we need to get hold of the sys object (actually it&#x27;s called a &quot;module&quot;) which allows us to do things with the system, like quit, which is what we&#x27;re going to do with it.&lt;&#x2F;p&gt;
&lt;p&gt;Open up LeadPad again, and immediately above the import pygame line, add this:&lt;&#x2F;p&gt;
&lt;pre&gt;import sys
&lt;&#x2F;pre&gt;
&lt;p&gt;Now we&#x27;re going to write a new function that allows us to exit the game immediately, whenever we want to. Just above the def ready_screen() line, add this new function:&lt;&#x2F;p&gt;
&lt;pre&gt;def quit():
    pygame.quit()
    sys.exit()
&lt;&#x2F;pre&gt;
&lt;p&gt;This function tells PyGame we are finishing (by calling pygame.quit), then exits: sys.exit() stops everything dead.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we make our waiting a bit posher, by editing the end function look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def end():
    pygame.event.clear()
    event_types_that_cancel = pygame.KEYDOWN, pygame.MOUSEBUTTONDOWN
    waiting = True
    while waiting:
        evt = pygame.event.wait()
        if evt.type == pygame.QUIT:
            quit()
        elif evt.type in event_types_that_cancel:
            waiting = False
&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s quite a lot here, but we&#x27;ll go through it line by line.&lt;&#x2F;p&gt;
&lt;p&gt;First, we clear the events as before using pygame.event.clear to make sure anything that has already happened doesn&#x27;t interfere with us.&lt;&#x2F;p&gt;
&lt;p&gt;Next we make a variable called event_types_that_cancel that is a list of all the types of events that we care about. They are key presses (KEYDOWN), and mouse clicks (MOUSEBUTTONDOWN). We&#x27;ll use this variable later.&lt;&#x2F;p&gt;
&lt;p&gt;Now we make another variable, called waiting. We use this to decide when we&#x27;ve finished. We set it to True, which means we are waiting, i.e. we haven&#x27;t finished.&lt;&#x2F;p&gt;
&lt;p&gt;The next line while waiting is the start of a loop, which means a piece of code we run lots of times. A &quot;while&quot; loop keeps on running until something happens. In this case, the lines after while waiting: will run again and again until the waiting variable becomes False.&lt;&#x2F;p&gt;
&lt;p&gt;The rest of the lines are all &quot;inside&quot; the loop, meaning they run again and again until the loop finishes. Notice that they are indented by a further 4 spaces to show that they are inside. If we had more lines later that were not indented, they would run after the loop had finished.&lt;&#x2F;p&gt;
&lt;p&gt;The first line creates a variable called evt that holds on to what came back when we called the function pygame.event.wait(). This function waits until an event happens. When an event happens, it could be a mouse movement, or a key press, or one of several other types of events. The old version of this code just called pygame.event.wait(), but didn&#x27;t look at what type of event happened. That&#x27;s why it finished even when you moved the mouse over the window. This new version captures the event that happened in the evt variable, and then does different things depending on what type of event it was.&lt;&#x2F;p&gt;
&lt;p&gt;The line starting with if lets us decide what to do based on what is happening. The first decision we make is evt.type == pygame.QUIT, which means if the type of event we have found is a special QUIT event. This type of event happens if the user closes the window by clicking the &quot;X&quot; in the top right-hand corner. If this happened, we go on to the next line, which calls the quit() function we created earlier. If this didn&#x27;t happen, we skip that line, and go on to the next one that is only indented as much as the original if line.&lt;&#x2F;p&gt;
&lt;p&gt;The next line after we skip is the one starting with elif. The &quot;condition&quot; we are checking (decision we are making) is whether evt.type in event_types_that_cancel which means is the event we have seen one of the ones we are interested it - is its type one of the things in event_types_that_cancel. event_types_that_cancel is a list containing the event type for mouse clicks, and the one for key presses, so if something interesting happened we will go to the next line, which is waiting = False. This sets waiting to False, meaning we will stop the loop. If the event was a different type (e.g. a mouse movement), we will skip the waiting = False line as well.&lt;&#x2F;p&gt;
&lt;p&gt;If neither of the conditions was true (so the event was not the user closing the window, or pressing a key or clicking the mouse) then we do nothing in the if and elif bit, and because we&#x27;re in a loop, we go back to the beginning and wait() again for another event. This goes on forever until one of the events we&#x27;re interested in happens. When it does, we either quit() immediately, or stop looping because waiting becomes False (meaning the while line lets us escape). When we&#x27;ve stopped looping we carry on, leaving the end function, and get to the end of the program.&lt;&#x2F;p&gt;
&lt;p&gt;If you can understand this you have just grasped two of the most important parts of programming - looping and conditional execution.&lt;&#x2F;p&gt;
&lt;p&gt;I suggest a long cup of tea.&lt;&#x2F;p&gt;
&lt;p&gt;While you&#x27;re sipping, try out our new program by opening LXTerminal as before, and typing the usual incantation:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice this time it doesn&#x27;t quit when you move the mouse, but it does if you click, or press a key, or close the window.&lt;&#x2F;p&gt;
&lt;p&gt;If it doesn&#x27;t work compare your program with my version: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-06&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Next time we&#x27;ll make an actual green shape appear, as promised a long time ago!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    bruce
    &lt;span class=&quot;comment_date&quot;&gt;2014-11-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy, thanks for the video series.. in my &quot;pi&quot; world, and i now understand the redgreen thanks to you. i am wanting to do a project for my son and my trainset where my train passes through the if or hall effect( i dont care which sensor), but then pi reads that break and sends a command to turn on the lights at the crossing gates, before the train gets there. once the trains clears the sensor, it waits so many seconds and releases the lights. so i can use your approach of &quot;assume its magic and it knows what to do&quot;, but i haven’t a clue as to how to call pins on and off, nor then how to take that state of &lt;sup&gt;1&lt;&#x2F;sup&gt;⁄&lt;sub&gt;0&lt;&#x2F;sub&gt; and turn on another relay from it..so from redgreen, this is level maybe 105…. any thoughts of the &quot;library&quot; of commands? it wold be interesting to design an image of the raspberry pi and when you (mouseover) a pin or part of the raspberry, it calls up the command associated with it.&lt;br&#x2F;&gt;
well in conclusion- i guess i gave examples of two different programs..&lt;&#x2F;p&gt;
&lt;p&gt;your thoughts are very much appreciated..&lt;&#x2F;p&gt;
&lt;p&gt;Bruce Humphrey&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-11-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bruce, this might help: &lt;a href=&quot;http:&#x2F;&#x2F;makezine.com&#x2F;projects&#x2F;tutorial-raspberry-pi-gpio-pins-and-python&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;makezine.com&#x2F;projects&#x2F;tutorial-raspber...&lt;&#x2F;a&gt;  Good luck with your project!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 05 &quot; Say something</title>
          <pubDate>Thu, 22 Nov 2012 08:31:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/22/my-first-raspberry-pi-game-part-05-say-something/</link>
          <guid>https://artificialworlds.net/blog/2012/11/22/my-first-raspberry-pi-game-part-05-say-something/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/22/my-first-raspberry-pi-game-part-05-say-something/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, 5, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 05 -- Say something&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;09c4c1bb-f2fa-4ed3-88ee-b1bbdace2145&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Today we will write some writing on that &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;blank screen&lt;&#x2F;a&gt; we made last time.&lt;&#x2F;p&gt;
&lt;p&gt;But first, a couple of tricks (we are doing &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;magic&lt;&#x2F;a&gt; after all). We&#x27;re going to make our program know it is a Python program, without needing to be told. To do this we need to do 2 things.&lt;&#x2F;p&gt;
&lt;p&gt;First, open up LeadPad as normal and add this line at the very (very) top of the file. Make sure there are no empty lines above it:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python
&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s a &quot;hash&quot; (or, for Americans, &quot;pound&quot;) symbol, followed by an exclamation mark. Note that all the slashes are forward slashes, not backward.&lt;&#x2F;p&gt;
&lt;p&gt;This tells our Raspberry Pi that this is a Python program. Now we need to tell our Pi that this program is allowed to run by itself, instead of its name being passed to the python program like we have been doing before.&lt;&#x2F;p&gt;
&lt;p&gt;To do this, open up LXTerminal as before, and type exactly this (and press Return):&lt;&#x2F;p&gt;
&lt;pre&gt;chmod +x redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;If all of this worked correctly, you should be able to run our program in a new way. Instead of typing python redgreen.py like we were before, we can type this in to LXTerminal:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s a dot, followed by a forward slash, followed by the name of our program.&lt;&#x2F;p&gt;
&lt;p&gt;This means run the program in this directory (that is the &quot;.&#x2F;&quot; part) called &quot;redgreen.py&quot;. Your Pi will look at redgreen.py and find the line we added that starts with &quot;#!&quot; and know to use Python to run it.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s get on with writing something on the screen. Go back to LeafPad and change the line starting with screen_size = to be these 3 lines:&lt;&#x2F;p&gt;
&lt;pre&gt;screen_width = 640
screen_height = 480
screen_size = screen_width, screen_height
&lt;&#x2F;pre&gt;
&lt;p&gt;This creates 3 variables - screen_width, screen_height and the one we had before screen_size, which is now made by putting the first 2 together. We&#x27;re going to use screen_height later.&lt;&#x2F;p&gt;
&lt;p&gt;Just below those 3 lines, type this:&lt;&#x2F;p&gt;
&lt;pre&gt;screen = None
ready_text = None
&lt;&#x2F;pre&gt;
&lt;p&gt;This gets 2 variables ready for us, and makes them empty. We&#x27;re going to fill them in inside start.&lt;&#x2F;p&gt;
&lt;p&gt;Change the start function to look exactly like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def start():
    &lt;span style=&quot;color: darkgreen&quot;&gt;global screen, ready_text&lt;&#x2F;span&gt;
    pygame.init()
    screen = pygame.display.set_mode( screen_size )
    &lt;span style=&quot;color: darkgreen&quot;&gt;font = pygame.font.Font( None, screen_height &#x2F; 5 )
    ready_text = font.render( &quot;Ready?&quot;, 1, pygame.Color( &quot;white&quot; ) )&lt;&#x2F;span&gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;The green lines above are the bits we&#x27;ve added. The line beginning global tells Python we want to work with those variables we got ready earlier inside this function, even though we created them outside it. (Without saying they were &quot;global&quot; we would be in danger of working with versions of them that only existed while we were inside the function, and disappeared as soon as we left.)&lt;&#x2F;p&gt;
&lt;p&gt;The font line makes a new font (a font is a typeface, or way of writing text). The first argument we passed was None because we don&#x27;t care at the moment which font we use (e.g. &quot;Arial&quot; or &quot;Times New Roman&quot;) - we are happy with the default. The second argument is for the size of the font we want, and we passed in screen_height &#x2F; 5, which means the value of the screen_height variable we created near the top, divided by 5. The &quot;&#x2F;&quot; character is how we write division in Python - it is supposed to look a bit like a fraction.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, on the last line we create another variable called ready_text, which contains the &quot;rendered&quot; version of the word &quot;Ready?&quot;, using the font we created, in white. &quot;Render&quot; means we create a picture showing the writing we wanted. We&#x27;ll draw this picture onto the screen in a second.&lt;&#x2F;p&gt;
&lt;p&gt;Now that all our preparation is over, we can finally write a fuller version of the ready_screen function. Change it to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def ready_screen():
    textpos = ready_text.get_rect(
        centerx = screen.get_width() &#x2F; 2,
        centery = screen.get_height() &#x2F; 2
    )

    screen.blit( ready_text, textpos )
    pygame.display.flip()
&lt;&#x2F;pre&gt;
&lt;p&gt;The first 4 lines (textpos up to the closing bracket all on its own) are really all one &quot;line of code&quot; - they are like one sentence of our program, that happens to span multiple lines. In Python if we want to continue a sentence (we call it a &quot;statement&quot;) we just leave a bracket unclosed. Python knows we have finished when we have closed all the brackets.&lt;&#x2F;p&gt;
&lt;p&gt;This &quot;line&quot; from textpost = up to ) creates a variable called textpos, which contains the place on the screen where we want to put our writing. We look inside ready_text (which is our rendered writing that we created above) for a function called get_rect that calculates a rectangle for us that is the right place on the screen to put the writing. The arguments we pass to get_rect are screen.get_width() &#x2F; 2 and screen.get_height() &#x2F; 2, which are telling it that it should calculate the rectangle by putting its centre in the middle of the screen. The middle of the screen is half-way across its width, and half-way down its height, which is why we are dividing the width and the height of the screen by 2.&lt;&#x2F;p&gt;
&lt;p&gt;Something worth noticing here is that the arguments to get_rect have names - we wrote centerx = and centery =. In Python we are allowed to give the names of arguments, or sometimes we can miss them out if we are happy just to put them in the right order. The get_rect function can actually take lots of different arguments, so it needs to know which ones you mean, which is why we named them.&lt;&#x2F;p&gt;
&lt;p&gt;Finally the last 2 lines do the real work. The screen.blit line tells PyGame to write the ready_text picture (the rendered writing) onto the screen at the position stored inside textpos. pygame.display.flip() is what we do to tell PyGame we&#x27;ve finished messing about with the screen, and we&#x27;re ready for it to display what we&#x27;ve done.&lt;&#x2F;p&gt;
&lt;p&gt;[The word &quot;blit&quot; is an oddity from the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Blit_%28computer_terminal%29&quot;&gt;olden days&lt;&#x2F;a&gt; which I&#x27;m afraid you&#x27;ll just have to memorise, and &quot;flip&quot; comes from the fact that behind the scenes there are really two screens - the one we are displaying, and the one we are working on. flip() switches them over, displaying the one we were working on, and making the other one ready to be worked on.]&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;ve got this far, well done! With any luck, we&#x27;re going to see the word &quot;Ready?&quot; on the screen in big letters.&lt;&#x2F;p&gt;
&lt;p&gt;Switch to LXTerminal again and type our new spell:&lt;&#x2F;p&gt;
&lt;pre&gt;.&#x2F;redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t move the mouse or press anything: a window should appear, with the word &quot;Ready?&quot; written in big white letters. When you press a key or move the mouse over it, it should disappear.&lt;&#x2F;p&gt;
&lt;p&gt;If something goes wrong, check back over the instructions carefully, and compare your file with this one: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-05&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    will
    &lt;span class=&quot;comment_date&quot;&gt;2013-11-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;i followed everything then i tryed it once and it worked then i tryed again to show my dad and it didnt work&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-11-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi will, what happened when it went wrong?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Louis
    &lt;span class=&quot;comment_date&quot;&gt;2013-12-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;hi Andy, thanks for all the tutorials they are great and help a ton!!!&lt;&#x2F;p&gt;
&lt;p&gt;one problem is for some reason since i tried the shortcut on EP.5 NONE of my programs will work. can i fix it or do i need to reset my raspberry pi?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-01-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Louis, what error messages do you see?  Try deleting the #! bit at the beginning and continuing without it – you can still launch it with python redgreen.py every time – the other way is just supposed to be easier.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Piet
    &lt;span class=&quot;comment_date&quot;&gt;2014-08-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy I did every thing right and it is not working i checked everything three times and it says&lt;br&#x2F;&gt;
traceback (most recent call last):&lt;br&#x2F;&gt;
Screen = none&lt;br&#x2F;&gt;
Name error: name &quot;none&quot; is not defined&lt;&#x2F;p&gt;
&lt;p&gt;PLEASE REPLY I HAVE COME TOO FAR&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Piet
    &lt;span class=&quot;comment_date&quot;&gt;2014-08-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy I did every thing right and it is not working i checked everything three times and it says&lt;br&#x2F;&gt;
traceback (most recent call last):&lt;br&#x2F;&gt;
File &quot;redgreen.py&quot;, line 9, in&lt;br&#x2F;&gt;
Screen = none&lt;br&#x2F;&gt;
Name error: name &quot;none&quot; is not defined&lt;&#x2F;p&gt;
&lt;p&gt;PLEASE REPLY I HAVE COME TOO FAR&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-08-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Piet, don’t give up!  You need to spell &quot;None&quot; with a capital &quot;N&quot;.  Also, make sure you check the case of all the other parts as well – I notice you’ve put a capital &quot;s&quot; for &quot;screen&quot;, but it should be lower case.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nigel Hughes
    &lt;span class=&quot;comment_date&quot;&gt;2012-12-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for giving us the text version.   The video is fine, but takes a lot of time and it is much easier to go back through the text version when you have blundered!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-12-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;No problem!  Yes, I find typing from video pretty error-prone.  I also sometimes explain things better in the text, or vice-versa.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Michael
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy.  Just wanted to thank you for your series of tutorials.  As a complete novice I am very grateful for the step by step introduction.  So is my 11 year old son, who has proudly published hi first &quot;Hello world&quot; on his diy website. He feels very accomplished.    &lt;a href=&quot;https:&#x2F;&#x2F;diy.org&#x2F;sparkboy123&#x2F;000sb9?utm_source=api&amp;amp;utm_medium=email&amp;amp;utm_campaign=project_new#50576&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;diy.org&#x2F;sparkboy123&#x2F;000sb9?utm_source...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Quick question though.  Is there a way to go back and change the set-up for the keyboard?  So of the keys are mixed up (the &quot; and the @ for example are reversed). No worries if you don’t have time for random questions.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks again.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Michael, please let your son know that I was so excited he got his &quot;Hello world&quot; program working I updated &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot; rel=&quot;nofollow&quot;&gt;Part 2&lt;&#x2F;a&gt; to link to his entry!&lt;&#x2F;p&gt;
&lt;p&gt;To change your keyboard settings (I think you have a US keyboard, but it defaulted to UK probably) I think the best thing to do is re-run the program you saw first time you booted, which you can do by opening LXTerminal and typing &quot;sudo raspi-config&quot; (without the quotes).  Let me know whether it works!&lt;&#x2F;p&gt;
&lt;p&gt;Thank you so much for your positive feedback.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;More details here by the way &lt;a href=&quot;http:&#x2F;&#x2F;elinux.org&#x2F;RPi_raspi-config&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;elinux.org&#x2F;RPi_raspi-config&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 04 &quot; A small black screen</title>
          <pubDate>Tue, 20 Nov 2012 19:01:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/20/my-first-raspberry-pi-game-part-04-a-small-black-screen/</link>
          <guid>https://artificialworlds.net/blog/2012/11/20/my-first-raspberry-pi-game-part-04-a-small-black-screen/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/20/my-first-raspberry-pi-game-part-04-a-small-black-screen/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, 4, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 04 -- A small black screen&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;36972c51-6f0c-4968-aa3e-faa8b9db345d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Today we will get an actual window to appear, with nothing on it!&lt;&#x2F;p&gt;
&lt;p&gt;Last time we wrote a &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;magic spell&lt;&#x2F;a&gt; describing the bare outline of what how our game will work. It&#x27;s going to pause, show a shape, and then the player must either press a key (if it&#x27;s green) or not press a key (if it&#x27;s red).&lt;&#x2F;p&gt;
&lt;p&gt;The program we&#x27;ve written calls several mini-programs called functions, but they don&#x27;t exist yet, and if we run it, it fails with an error. The names of the functions we have imagined are start, ready_screen, wait, shape and end.&lt;&#x2F;p&gt;
&lt;p&gt;The first thing we need to do is write those functions, so that the program runs correctly, even though it does nothing.&lt;&#x2F;p&gt;
&lt;p&gt;Open up LeafPad again and open redgreen.py from inside the pi folder. At the top, above the code we&#x27;ve already written (which uses the functions) type exactly this:&lt;&#x2F;p&gt;
&lt;pre&gt;def start():
    pass

def ready_screen():
    pass

def wait():
    pass

def shape():
    pass

def end():
    pass
&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that the word pass is &quot;indented&quot; by 4 spaces. This is how Python knows which lines of code are part of your function, and which are another part of the program. It&#x27;s very important that all lines are indented by the same amount, and that you never mix &quot;tab&quot; characters with spaces. To make life simple, I suggest always using 4 spaces.&lt;&#x2F;p&gt;
&lt;p&gt;What we&#x27;ve done is define 5 functions (the ones we call further down). def just means define a function, and we&#x27;ve followed that by the name of the function, then (). So far, each of our functions is empty: they are all just one line, which says pass, which means &quot;do nothing&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If we&#x27;ve typed this correctly, our program should now run correctly, and do nothing. Let&#x27;s try it. Open LXTerminal and type:&lt;&#x2F;p&gt;
&lt;pre&gt;python redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;If it&#x27;s working, the computer should say absolutely nothing to you, and give you another prompt, ending with $.&lt;&#x2F;p&gt;
&lt;p&gt;If you get an error message, read it (especially the last line) and try to work out what you typed wrong.&lt;&#x2F;p&gt;
&lt;p&gt;Now we&#x27;re going to write some actual useful code, and make a black screen appear.&lt;&#x2F;p&gt;
&lt;p&gt;At the very top, above everything else we&#x27;ve done today, add this line:&lt;&#x2F;p&gt;
&lt;pre&gt;import pygame
&lt;&#x2F;pre&gt;
&lt;p&gt;This tells Python we want to use the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.pygame.org&quot;&gt;PyGame&lt;&#x2F;a&gt; library, which is some code written by someone else, designed to help us write games in Python. The Raspberry Pi comes with PyGame already installed, so to use it all we need to do is add this line.&lt;&#x2F;p&gt;
&lt;p&gt;Just below that line, type this:&lt;&#x2F;p&gt;
&lt;pre&gt;screen_size = 640, 480
&lt;&#x2F;pre&gt;
&lt;p&gt;This is how we define a &quot;variable&quot;. A variable is a thing with a name and a value that we can refer back to later. The variable&#x27;s name is screen_size and its value is &quot;640, 480&quot;. We&#x27;re going to use this variable to remember how big we want our window to be.&lt;&#x2F;p&gt;
&lt;p&gt;Now we need to modify our start function to bring up a black screen. Replace both lines of the start function with this:&lt;&#x2F;p&gt;
&lt;pre&gt;def start():
    pygame.init()
    screen = pygame.display.set_mode( screen_size )
&lt;&#x2F;pre&gt;
&lt;p&gt;The first line, starting with def is the same as before - it means we are defining a function called &quot;start&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;Where we used to have just pass, we now have 2 lines (both indented by 4 spaces as before). The pygame.init() line just tells PyGame to get ready to start. We need something like this in our program whenever we use PyGame. The second line creates a window of the size we want. It calls a function inside pygame. The dots mean we should &quot;look inside&quot; the thing on the left, so pygame.display.set_mode means something like &quot;look inside pygame for something called display, then look inside display for something called set_mode&quot;. set_mode is a function, which we are calling (that is what the brackets mean).&lt;&#x2F;p&gt;
&lt;p&gt;Unlike other functions we have called, which just use &quot;()&quot;, set_mode takes an &quot;argument&quot; which means we are passing information to it. The information we are passing is the size of the screen, which we have already stored in the variable screen_size. So the value &quot;640, 480&quot; gets passed in to set_mode, telling it how big to make the window.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve made a window, but if we leave things as they are now, we will open it, then immediately finish, so it will go away again. We need to wait a while.&lt;&#x2F;p&gt;
&lt;p&gt;Edit the end function to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;def end():
    pygame.event.clear()
    pygame.event.wait()
&lt;&#x2F;pre&gt;
&lt;p&gt;This code means &quot;wait until something happens&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;The first line (clear) tells PyGame we&#x27;re not interested in anything that happened before - clear the list of events that has built up - and the second line (wait) means wait until something happens. In this case we are waiting until the person using the program presses a key, closes the window, or even moves the mouse in front of the window.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s try it out. Open up LXTerminal as before, and type the same thing we&#x27;ve typed before:&lt;&#x2F;p&gt;
&lt;pre&gt;python redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;Make sure you don&#x27;t move the mouse or press any keys after you&#x27;ve pressed Return. If all has gone well, a small, black, empty window will appear. Then if you press a key or move the mouse in front of it, it should disappear.&lt;&#x2F;p&gt;
&lt;p&gt;If this isn&#x27;t what happens, check back and make sure you typed everything exactly as above. Your complete file should look like this: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;raw.github.com&#x2F;andybalaam&#x2F;redgreen&#x2F;lesson-04&#x2F;redgreen.py&quot;&gt;redgreen.py&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Well done! You made a window appear. Next time, if we&#x27;re lucky, we&#x27;ll put something in it...&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nate
    &lt;span class=&quot;comment_date&quot;&gt;2017-07-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello, Andy. I can’t get the black small screen to appear. The terminal on my pi says&lt;br&#x2F;&gt;
import: command not found&lt;br&#x2F;&gt;
screen_size: command not found&lt;br&#x2F;&gt;
and some other syntax errors. The terminal is running python 3.4.2 and I’ve tried upgrading the pip and pygame to no avail.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-07-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Nate, it sounds like you are trying to type your program directly into the terminal.  You need to edit your program in the text editor (Leafpad), save it, and run it by typing &quot;python redgreen.py&quot; in the terminal.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jimmy Saldivias
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello Andy.&lt;br&#x2F;&gt;
I edited the program, using 4 spaces. When I asked to run the message on line 2 there was IndentationError: unexpected indent.&lt;br&#x2F;&gt;
So I removed the 4 spaces from the line 2 and kept the 4 spaces for the rest of lines. The message of error in line 2 dissapeared.&lt;br&#x2F;&gt;
But the error appeared again this time for line 5. So I removed the 4 spaces from all lines.&lt;br&#x2F;&gt;
The error messaged changed for a the one that said NamError: name ‘start’ is not defined.&lt;br&#x2F;&gt;
I am using Python2.&lt;br&#x2F;&gt;
Any suggestions?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jimmy Saldivias
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy.&lt;br&#x2F;&gt;
My mistake. I have not used def before the names and (): after the names.&lt;br&#x2F;&gt;
Now it is working.&lt;br&#x2F;&gt;
Thanks.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jimmy Saldivias
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy.&lt;br&#x2F;&gt;
The link to PREVIOUS is not correct.&lt;br&#x2F;&gt;
The same happens to the windows to NEXT of the forecoming lesson.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Jimmy, glad to hear you got it working!.  Those Next and Previous links are just whichever blog posts I wrote at that time, not next and previous in this series, and I can’t change them because they are made automatically by my blog software (WordPress).  The links at the top (&quot;1&quot;, &quot;2&quot;, …) are correct though!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Length of Open Source licenses</title>
          <pubDate>Thu, 15 Nov 2012 13:34:34 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/15/length-of-open-source-licenses/</link>
          <guid>https://artificialworlds.net/blog/2012/11/15/length-of-open-source-licenses/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/15/length-of-open-source-licenses/">&lt;p&gt;I have been choosing a license for my ficticious programming language, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;pepper&quot;&gt;Pepper&lt;&#x2F;a&gt;. One consideration is the complexity of the (combination of) license(s) used. Complexity may be related to length, so for your enjoyment, here are the lengths of some popular licenses:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;license-length.png&quot; alt=&quot;Length of Open Source Licenses&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Is it a coincidence I&#x27;ve decided to use the MIT License?&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how I made it (on my Ubuntu Linux 12.04 system):&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

declare -A LICENSES

LICENSES[Apache]=file:&#x2F;&#x2F;&#x2F;usr&#x2F;share&#x2F;common-licenses&#x2F;Apache-2.0
LICENSES[GPL2]=file:&#x2F;&#x2F;&#x2F;usr&#x2F;share&#x2F;common-licenses&#x2F;GPL-2
LICENSES[GPL3]=file:&#x2F;&#x2F;&#x2F;usr&#x2F;share&#x2F;common-licenses&#x2F;GPL-3
LICENSES[BSD]=file:&#x2F;&#x2F;&#x2F;usr&#x2F;share&#x2F;common-licenses&#x2F;BSD
LICENSES[Artistic]=file:&#x2F;&#x2F;&#x2F;usr&#x2F;share&#x2F;common-licenses&#x2F;Artistic

LICENSES[MIT]=http:&#x2F;&#x2F;www.jclark.com&#x2F;xml&#x2F;copying.txt
LICENSES[Mozilla]=http:&#x2F;&#x2F;www.mozilla.org&#x2F;MPL&#x2F;2.0&#x2F;index.txt
LICENSES[Python]=http:&#x2F;&#x2F;hg.python.org&#x2F;cpython&#x2F;raw-file&#x2F;tip&#x2F;LICENSE
LICENSES[Eclipse]=http:&#x2F;&#x2F;www.eclipse.org&#x2F;legal&#x2F;epl-v10.html

function wordcounts()
{
	for K in &quot;${!LICENSES[@]}&quot;; do
	{
		echo `lynx -dump ${LICENSES[$K]} | wc -w` $K
	}; done
}

wordcounts | sort -n | awk &#x27;{print $2 &quot; &quot; $1}&#x27; &amp;gt; data.txt

gnuplot &amp;lt;&amp;lt;END
set terminal png large enhanced font &quot;Helvetica,11&quot;
set output &#x27;license-length.png&#x27;
unset key
set style fill solid
set title &quot;Number of words in common Free and Open Source licenses&quot;
plot &#x27;data.txt&#x27; using (column(0)):2:(0.5):(\$0):xtic(1) with boxes lc variable;
END

rm data.txt
&lt;&#x2F;pre&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Phil Nash
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Shame you didn’t include the boost licence. I’d like to see how that stacks up (It would certainly be on the far left somewhere)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;BOOST is 215 – just to the right of BSD I think.  I considered BOOST, but went for MIT as it’s more widely used.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 03 &quot; It&#x27;s like a magic spell</title>
          <pubDate>Wed, 14 Nov 2012 08:27:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/14/my-first-raspberry-pi-game-part-03-its-like-a-magic-spell/</link>
          <guid>https://artificialworlds.net/blog/2012/11/14/my-first-raspberry-pi-game-part-03-its-like-a-magic-spell/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/14/my-first-raspberry-pi-game-part-03-its-like-a-magic-spell/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, 3, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 03 -- It&#x27;s like a magic spell&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;510cfb41-1968-4e73-bbc7-e11b52a5519c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;Today we will write the very basic outline of our game. When we&#x27;ve finished it won&#x27;t actually do anything. In fact, it won&#x27;t even run.&lt;&#x2F;p&gt;
&lt;p&gt;Writing a computer program is a lot like doing magic. To write programs well it is really helpful to treat it like magic, and not think about how it works (until you have to...). We&#x27;re going to do that - write down in very simple terms what our program will do, but not yet think about how it will do it.&lt;&#x2F;p&gt;
&lt;p&gt;The program we are going to write is a very simple test of your reactions. It will show you either a green circle or a red square. If you see a green circle, you have to press a key as quickly as you can. If you see a red square, you must not press anything.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the whole of the game (for now).&lt;&#x2F;p&gt;
&lt;p&gt;To make this game, we need to start up, then show a ready screen, then wait for a while showing the ready screen, then show a shape and wait for a key press, then finish.&lt;&#x2F;p&gt;
&lt;p&gt;So, let&#x27;s write our magic spell. Start up LeafPad just like we did in &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;part 2&lt;&#x2F;a&gt;. Click File, then Open, click on &quot;pi&quot; on the left, then double-click redgreen.py to load up our Hello, world program. Delete everything that&#x27;s there, and type exactly this instead:&lt;&#x2F;p&gt;
&lt;pre&gt;start()

ready_screen()

wait()

shape()

end()
&lt;&#x2F;pre&gt;
&lt;p&gt;The brackets after each word mean &quot;do it&quot; - what they really mean is find a &quot;function&quot; with this name, and run it. A function is like a mini-program.&lt;&#x2F;p&gt;
&lt;p&gt;So, now our program is finished, right?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s run it. Go to the File menu in LeafPad and click Save, then open LXTerminal just like in part 2 and run the program by typing &quot;python redgreen.py&quot; as before. Here&#x27;s what happens:&lt;&#x2F;p&gt;
&lt;pre&gt;$ python redgreen.py
Traceback (most recent call last):
  File &quot;redgreen.py&quot;, line 3, in &lt;module&gt;
    start()
NameError: name &#x27;start&#x27; is not defined
&lt;&#x2F;module&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We told python to run a function called &quot;start&quot;, but we haven&#x27;t written any functions yet, so it couldn&#x27;t find it.&lt;&#x2F;p&gt;
&lt;p&gt;Our spell is cast, but we next time we need to start on the ingredients it uses. See you then.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam&amp;#x27;s Blog » Blog Archive » My First Raspberry Pi Game â€“ Part 02 â€“ Saying hello
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Free Software      « My First Raspberry Pi Game – Part 01 – Before we start My First Raspberry Pi Game â€“ Part 03 â€“ It’s like a magic spell […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bill Gentles
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is very helpful. I’m a total newbie at Linux. Thanks for making things so clear. Hope there’s more to come.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Bill, thanks!  I am working on the next one…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam&amp;#x27;s Blog » Blog Archive » My First Raspberry Pi Game – Part 01 – Before we start
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] My First Raspberry Pi Game – Part 01 – Before we start      Parts: 1, 2, 3. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mark Kelly
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is very easy to follow. I look forward to the next one and many more after that. You have a talent for teaching.&lt;br&#x2F;&gt;
So many others have this look at lot more complicated than it is. You have done the clever thing of making it seem simple; not an easy task to do.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Mark!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game &quot; Part 02 &quot; Saying hello</title>
          <pubDate>Tue, 06 Nov 2012 08:45:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/11/06/my-first-raspberry-pi-game-part-02-saying-hello/</link>
          <guid>https://artificialworlds.net/blog/2012/11/06/my-first-raspberry-pi-game-part-02-saying-hello/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/11/06/my-first-raspberry-pi-game-part-02-saying-hello/">&lt;p&gt;Parts: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;1&lt;&#x2F;a&gt;, 2, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Writing your first ever computer program on the Raspberry Pi. See &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; for how to get and set up the Pi.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Today we will find out how to write a computer program, and how to run it.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 02 -- Saying hello&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0fa51801-9074-400e-97e9-4e5eb43fdb37&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We&#x27;re going to write one of the simplest programs you can write - we&#x27;re going to get the computer to say hello to us.&lt;&#x2F;p&gt;
&lt;p&gt;First, we need a text editor to write down our program. Click the weird aeroplane-y thing in the bottom left - that brings up the menu (like the Start button on Windows), then choose Accessories, then Leafpad. Leafpad is the text editor we will be using.&lt;&#x2F;p&gt;
&lt;p&gt;Leafpad will start, and show you an empty page. This is where we will write our program.&lt;&#x2F;p&gt;
&lt;p&gt;Type in exactly this:&lt;&#x2F;p&gt;
&lt;pre&gt;print &quot;Hello, world!&quot;
&lt;&#x2F;pre&gt;
&lt;p&gt;and then click the File menu at the top of the Leafpad window and choose Save As. Click the word &quot;pi&quot; on the left and then click in the empty box next to the word Name, and type the name of our program, which is:&lt;&#x2F;p&gt;
&lt;pre&gt;redgreen.py&lt;&#x2F;pre&gt;
&lt;p&gt;&quot;redgreen&quot; is the name and the &quot;.py&quot; means this is a program written in the language &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.python.org&#x2F;&quot;&gt;Python&lt;&#x2F;a&gt;. We&#x27;ll be finding out more about Python as we go on.&lt;&#x2F;p&gt;
&lt;p&gt;Click the Save button.&lt;&#x2F;p&gt;
&lt;p&gt;Our program is finished! Now we need to run it.&lt;&#x2F;p&gt;
&lt;p&gt;Click the aeroplane-y thing again, then Accessories, then LXTerminal. A terminal is a program you use to run other programs.&lt;&#x2F;p&gt;
&lt;p&gt;When LXTerminal has started, your cursor will appear next to a $ sign. This means it is ready for you to tell it what to do.&lt;&#x2F;p&gt;
&lt;p&gt;Type exactly this:&lt;&#x2F;p&gt;
&lt;pre&gt;python redgreen.py
&lt;&#x2F;pre&gt;
&lt;p&gt;What this means is run the program called &quot;python&quot;, and pass the name of our program (redgreen.py) to it. This is how you run Python programs.&lt;&#x2F;p&gt;
&lt;p&gt;Now press the RETURN key.&lt;&#x2F;p&gt;
&lt;p&gt;If all goes well, our program will talk back to us, and say what we told it to say:&lt;&#x2F;p&gt;
&lt;pre&gt;Hello, world!
&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s look again at our program.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s just one &quot;statement&quot;, a print statement. A statement is something to do.&lt;&#x2F;p&gt;
&lt;p&gt;We pass one &quot;argument&quot; to print, &quot;Hello, world!&quot;. An argument is some information you give to a statement.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;print&quot; doesn&#x27;t mean print to the printer, but write to the screen. So our program did exactly what we told it to do - it wrote our message to the screen.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll map out the whole of our real program - a simple game.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Update: congratulations to sparkboy123 on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;diy.org&#x2F;sparkboy123&#x2F;000sb9?utm_source=api&amp;amp;utm_medium=email&amp;amp;utm_campaign=project_new#50576&quot;&gt;getting this working&lt;&#x2F;a&gt;!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jimmy Saldivias
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy.&lt;br&#x2F;&gt;
I decided to do the exercises. I found out that they don’t work with python 3. But as you mentioned in the comments on the previous chapter, it works fine with python 2. I did another mistake -in case anyone else does the same-. I used uppercase. So, things did not work until I used lower case for the command and for the name of the file. Thanks for the videos and the tutorial.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My First Raspberry Pi Game - Part 01 - Before we start</title>
          <pubDate>Tue, 30 Oct 2012 20:50:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/10/30/my-first-raspberry-pi-game-part-01-before-we-star/</link>
          <guid>https://artificialworlds.net/blog/2012/10/30/my-first-raspberry-pi-game-part-01-before-we-star/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/10/30/my-first-raspberry-pi-game-part-01-before-we-star/">&lt;p&gt;Parts: 1, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;06&#x2F;my-first-raspberry-pi-game-part-02-saying-hello&#x2F;&quot;&gt;2&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;14&#x2F;my-first-raspberry-pi-game-part-03-its-like-a-magic-spell&#x2F;&quot;&gt;3&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;20&#x2F;my-first-raspberry-pi-game-part-04-a-small-black-screen&#x2F;&quot;&gt;4&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;22&#x2F;my-first-raspberry-pi-game-part-05-say-something&#x2F;&quot;&gt;5&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;24&#x2F;my-first-raspberry-pi-game-part-06-a-better-class-of-waiting&#x2F;&quot;&gt;6&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;11&#x2F;28&#x2F;my-first-raspberry-pi-game-part-07-a-green-circle&#x2F;&quot;&gt;7&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;04&#x2F;my-first-raspberry-pi-game-part-08-success-and-failure&#x2F;&quot;&gt;8&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;08&#x2F;my-first-raspberry-pi-game-part-09-lots-more-words&#x2F;&quot;&gt;9&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;12&#x2F;14&#x2F;my-first-raspberry-pi-game-part-10-red-square&#x2F;&quot;&gt;10&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;11&#x2F;my-first-raspberry-pi-game-part-11-being-less-rude&#x2F;&quot;&gt;11&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;01&#x2F;16&#x2F;my-first-raspberry-pi-game-part-12-scoring-done&#x2F;&quot;&gt;12&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In this series I intend to guide you through writing your first ever computer program.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;My First Raspberry Pi Game -- Part 01 -- Before we start&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;a6562a7c-9906-406f-9b21-12647de18305&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;We will write our own game on the Raspberry Pi, which is a cheap-as-chips computer designed for learning about computers.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-a-raspberry-pi&quot;&gt;Get a Raspberry Pi&lt;&#x2F;h3&gt;
&lt;p&gt;To follow along, here&#x27;s what you will need:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A Raspberry Pi (about Â£25) - I got it from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;uk.rs-online.com&#x2F;web&#x2F;generalDisplay.html?id=raspberrypi&amp;amp;cm_mmc=UK-Short_url-_-Internal-Flyer-_-%20RaspberryPie-062012-_-Multimedia&quot;&gt;RS&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;An SD card (about Â£10) - be careful - not all of them work. I use: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.co.uk&#x2F;gp&#x2F;product&#x2F;B004TS1IG4&#x2F;ref=oh_details_o00_s00_i00&quot;&gt;Kingston Technology 16GB&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;If your TV supports it, an HDMI cable (about Â£1) - I got: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.co.uk&#x2F;gp&#x2F;product&#x2F;B0017RW94A&#x2F;ref=oh_details_o00_s00_i00&quot;&gt;HDMI to HDMI Connector&lt;&#x2F;a&gt;. If your TV doesn&#x27;t support HDMI, get a composite cable, but it won&#x27;t look as good or work as well.&lt;&#x2F;li&gt;
&lt;li&gt;A power supply (about Â£4) e.g. a smartphone charger (micro USB, at least 5V, 1A) - I got &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.co.uk&#x2F;gp&#x2F;product&#x2F;B0054QBPVS&#x2F;ref=oh_details_o06_s00_i00&quot;&gt;Micro USB Mains Charger&lt;&#x2F;a&gt; but my existing HTC Wildfire S charger worked too.&lt;&#x2F;li&gt;
&lt;li&gt;USB keyboard and mouse (about Â£7) - I had them lying around, but a quick search suggests this one might be ok: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.co.uk&#x2F;CiT-USB-Keyboard-Mouse-Combo&#x2F;dp&#x2F;B00647IDCU&#x2F;ref=sr_1_1?s=computers&amp;amp;ie=UTF8&amp;amp;qid=1351151311&amp;amp;sr=1-1&quot;&gt;CiT USB Keyboard and Mouse&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;(Total cost, very approximately: Â£47)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;install-raspian&quot;&gt;Install &quot;Raspian&quot;&lt;&#x2F;h3&gt;
&lt;p&gt;To use the Pi you will need to install some software onto your SD card.&lt;&#x2F;p&gt;
&lt;p&gt;To do this you will need a PC or laptop. If you don&#x27;t have one, or you&#x27;d prefer not to download and install software to an SD card, check out &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;thepihut.com&#x2F;&quot;&gt;The Pi Hut&lt;&#x2F;a&gt;. They sell SD cards that already have Raspian installed on them.&lt;&#x2F;p&gt;
&lt;p&gt;Raspian is the name of the software we will use to start up and run our Raspberry Pi. You need to download it and install it onto your SD card before you can put the SD card into the Pi and turn it on. To do this, you&#x27;ll need a way of writing to the SD card. Lots of laptops (and some desktops) have built-in SD card readers, or you can get a USB reader (I got this one: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.amazon.co.uk&#x2F;gp&#x2F;css&#x2F;order-history&#x2F;ref=ohs_order_orderid?ie=UTF8&amp;amp;opt=ab&amp;amp;qid=&amp;amp;search=736-5538608-1555312&amp;amp;sr=&quot;&gt;SD Card Reader USB 2.0&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;To install Raspian &quot;wheezy&quot; (wheezy is the name of the latest version) go to the Raspberry Pi download page at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.raspberrypi.org&#x2F;downloads&quot;&gt;www.raspberrypi.org&#x2F;downloads&lt;&#x2F;a&gt; and click the link in the Raspian &quot;wheezy&quot; section next to the words &quot;Direct download&quot;. Follow the instructions on how to install Raspian to your SD card here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elinux.org&#x2F;RPi_Easy_SD_Card_Setup&quot;&gt;elinux.org&#x2F;RPi_Easy_SD_Card_Setup&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;(There are also some helpful instructions here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;reviews.cnet.co.uk&#x2F;desktops&#x2F;how-to-get-started-with-the-raspberry-pi-50009845&#x2F;&quot;&gt;reviews.cnet.co.uk&#x2F;desktops&#x2F;how-to-get-started-with-the-raspberry-pi-50009845&#x2F;&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;start-the-pi&quot;&gt;Start the Pi&lt;&#x2F;h3&gt;
&lt;p&gt;Once you&#x27;ve got an SD card with Raspian on it, insert it into your Pi (the SD card slot is underneath, which surprised me a bit). Plug the Pi into your TV by connecting the HDMI cable to it and plugging the other end into the TV&#x27;s HDMI port. Plug your keyboard and mouse into the 2 USB slots.&lt;&#x2F;p&gt;
&lt;p&gt;Take a deep breath, and plug the power supply into the micro-USB port.&lt;&#x2F;p&gt;
&lt;p&gt;If all goes well, some lights will appear on the Pi, you will be able to switch your TV to HDMI mode and your screen will show some writing and possibly pictures of raspberries. Wait for it all to settle down, and (hopefully) eventually you&#x27;ll see the setup screen.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;first-time-setup&quot;&gt;First time setup&lt;&#x2F;h3&gt;
&lt;p&gt;The first time your Pi boots it will ask you to do some setup. Read the raspi-config menu items and see whether there&#x27;s anything you want to change. You might want to change your keyboard and language settings, but I didn&#x27;t need to change anything at all. I just pressed TAB and then right-arrow to move onto the word Finish, then pressed RETURN.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s more information about how to set everything up at &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;elinux.org&#x2F;RPi_raspi-config&quot;&gt;elinux.org&#x2F;RPi_raspi-config&lt;&#x2F;a&gt;, and there&#x27;s a nice detailed video here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=nXvODj6YpAE&quot;&gt;First boot and Raspi-config&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Wait a bit more, and eventually you should see a huge raspberry, with a mouse cursor and desktop. If so, you&#x27;re ready for the next part!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;: a real person really following this series!:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;@&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;andybalaam&quot;&gt;andybalaam&lt;&#x2F;a&gt; my lad @&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;irrelevawesome&quot;&gt;irrelevawesome&lt;&#x2F;a&gt; just got a Rpi. He&#x27;s using your tutorial now. Thanks! &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;t.co&#x2F;V9iORdUp&quot;&gt;twitter.com&#x2F;foobarber&#x2F;statâ€¦&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;— Pete Barber (@foobarber) &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;foobarber&#x2F;status&#x2F;295193554337280001&quot;&gt;January 26, 2013&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Thrifting with Raspberry Pi | Dive Into the Mind of a Middle School Teacher
    &lt;span class=&quot;comment_date&quot;&gt;2013-08-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] I found an awesome website to create my first game! It took me two hours to complete and was pretty simple. If a green circle […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jimmy Saldivias
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is a great place to be. I have been working with Raspberry Pi 3 for a year now. And nowhere i found as structured approach to programming as yours. It is easy to follow. Each step is explained thoroughly. At the same time you do not delve more than is necessary to explain a concept. Directing us to places where we can find more info if needed or wanted. I particularly like the &quot;magic&quot; approach. Thanks.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Colin Brearley
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy, hope you are all well. We got J a Pi for Christmas so will be getting him to try this out this weekend!!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Colin, we are very well, good luck with it, and leave comments if things are unclear.  It’s a bit out of date, but hopefully most of it still makes sense.  It uses Python 2, so if Python 3 is the default on the Pi 3, that will cause you problems.  if that is the situation, you can make it all work by just typing &quot;python2&quot; to launch, instead of &quot;python&quot;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ken Roberts
    &lt;span class=&quot;comment_date&quot;&gt;2012-10-31&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy,&lt;br&#x2F;&gt;
This is wonderful.  You are a great teacher.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-10-31&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Ken :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Cool Raspberry Pi Projects – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2012-12-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Update: I made some videos on how to write your first ever game on your Raspberry Pi! […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Running Dojo DOH tests in a browser without a web server</title>
          <pubDate>Tue, 30 Oct 2012 10:46:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/10/30/running-dojo-doh-tests-in-a-browser-without-a-web-server/</link>
          <guid>https://artificialworlds.net/blog/2012/10/30/running-dojo-doh-tests-in-a-browser-without-a-web-server/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/10/30/running-dojo-doh-tests-in-a-browser-without-a-web-server/">&lt;p&gt;Dojo&#x27;s DOH requires a web server to run tests in a browser. But never fear:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cd ~&#x2F;code&#x2F;dojo
$ ls
docs  dojo  util
$ python -m SimpleHTTPServer &amp;amp;
$ xdg-open http:&#x2F;&#x2F;localhost:8000&#x2F;util&#x2F;doh&#x2F;runner.html
&lt;&#x2F;pre&gt;
&lt;p&gt;Note that you will see some test failures, because the python web server doesn&#x27;t do PHP.&lt;&#x2F;p&gt;
&lt;p&gt;When finished:&lt;&#x2F;p&gt;
&lt;pre&gt;$ kill %1
&lt;&#x2F;pre&gt;
&lt;p&gt;to stop your web server.&lt;&#x2F;p&gt;
&lt;p&gt;On Python 3, use this instead of the SimpleHTTPServer line:&lt;&#x2F;p&gt;
&lt;pre&gt;python3 -m http.server &amp;amp;
&lt;&#x2F;pre&gt;
&lt;p&gt;Yes, Python includes a little web server that serves files in your current directory. Batteries included. Thanks to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.lylebackenroth.com&#x2F;blog&#x2F;2009&#x2F;05&#x2F;03&#x2F;serve-your-current-directory-using-a-simple-webserver-python&#x2F;&quot;&gt;Lyle Backenroth&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.commandlinefu.com&#x2F;commands&#x2F;view&#x2F;71&#x2F;serve-current-directory-tree-at-httphostname8000&quot;&gt;commandlinefu&lt;&#x2F;a&gt; for making me aware of this.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Paul Lysak
    &lt;span class=&quot;comment_date&quot;&gt;2012-11-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you, it helped me a lot to make a testable distribution of my CSV parsing library.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Bash associative array examples</title>
          <pubDate>Wed, 17 Oct 2012 12:30:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/10/17/bash-associative-array-examples/</link>
          <guid>https://artificialworlds.net/blog/2012/10/17/bash-associative-array-examples/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/10/17/bash-associative-array-examples/">&lt;p&gt;Quick reference of things I discovered about how to use associative arrays in bash. &lt;strong&gt;Note: bash version 4 only.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(See also &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;09&#x2F;18&#x2F;bash-arrays&#x2F;&quot;&gt;Bash Arrays&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;basics&quot;&gt;Basics&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP     # Create an associative array
$ MYMAP[foo]=bar       # Put a value into an associative array
$ echo ${MYMAP[foo]}   # Get a value out of an associative array
bar
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo MYMAP[foo]&lt;&#x2F;span&gt;      # WRONG
MYMAP[foo]
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo $MYMAP[foo]&lt;&#x2F;span&gt;     # WRONG
[foo]
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;creating&quot;&gt;Creating&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP     # Explicitly declare
$ MYMAP[foo]=bar       # Or this line implicitly makes it an associative array (in global scope, bash 4.2+ only)
$ MYMAP[baz]=quux      # Can add multiple values one by one
$ MYMAP[corge]=grault
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -A MYMAP=( [foo]=bar [baz]=quux [corge]=grault ) # Initialise all at once
&lt;p&gt;$ echo ${MYMAP[foo]}
bar
$ echo ${MYMAP[baz]}
quux
&lt;&#x2F;pre&gt;&lt;pre&gt;$ declare -A MYMAP                              # Or declare separately
$ MYMAP=( [foo]=bar [baz]=quux [corge]=grault ) # Then initialise
$ echo ${MYMAP[foo]}
bar
$ echo ${MYMAP[baz]}
quux
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;variables-as-keys&quot;&gt;Variables as keys&lt;&#x2F;h3&gt;
&lt;pre&gt;$ K=baz
$ MYMAP[$K]=quux       # Use a variable as key to put a value into an associative array
$ echo ${MYMAP[$K]}    # Use a variable as key to extract a value from an associative array
quux
$ echo ${MYMAP[baz]}   # Obviously the value is accessible via the literal key
quux
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;quoting-keys&quot;&gt;Quoting keys&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP
$ MYMAP[foo A]=&quot;bar B&quot;         # Quoting keys makes no difference
$ MYMAP[&quot;corge X&quot;]=&quot;grault Y&quot;  # Quoting keys makes no difference
$ echo ${MYMAP[&quot;corge X&quot;]}     # You can access by quoting
grault Y
$ echo ${MYMAP[corge X]}       # Or not bother
grault Y
$ echo ${MYMAP[foo A]}
bar B

$ MYMAP[&#x27;waldo 1&#x27;]=&quot;fred 2&quot;    # Single quotes also make no difference
$ echo ${MYMAP[&#x27;waldo 1&#x27;]}
fred 2
$ echo ${MYMAP[waldo 1]}
fred 2

$ K=plugh
$ MYMAP[&#x27;$K&#x27;]=xyzzy   # Except single quotes prevent variable expansion, as usual
$ echo ${MYMAP[plugh]}

$ echo ${MYMAP[&#x27;$K&#x27;]}
xyzzy
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;missing-keys&quot;&gt;Missing keys&lt;&#x2F;h3&gt;
&lt;pre&gt;$ MYMAP[foo]=bar
$ echo ${MYMAP[missing]}   # Accessing a missing value gives &quot;&quot;

$ # Testing whether a value is missing from an associative array
$ if [ ${MYMAP[foo]+_} ]; then echo &quot;Found&quot;; else echo &quot;Not found&quot;; fi
Found
$ if [ ${MYMAP[missing]+_} ]; then echo &quot;Found&quot;; else echo &quot;Not found&quot;; fi
Not found
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;looping&quot;&gt;Looping&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP=( [foo a]=bar [baz b]=quux )
$ echo &quot;${!MYMAP[@]}&quot;  # Print all keys - quoted, but quotes removed by echo
foo a baz b

$ # Loop through all keys in an associative array
$ for K in &quot;${!MYMAP[@]}&quot;; do echo $K; done
foo a
baz b

$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;for K in ${!MYMAP[@]}; do echo $K; done&lt;&#x2F;span&gt;     # WRONG
foo
a
baz
b

$ # Looping through keys and values in an associative array
$ for K in &quot;${!MYMAP[@]}&quot;; do echo $K --- ${MYMAP[$K]}; done
foo a --- bar
baz b --- quux

$ # Loop through all values in an associative array
$ for V in &quot;${MYMAP[@]}&quot;; do echo $V; done
bar
quux
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;clearing&quot;&gt;Clearing&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP
$ MYMAP[foo]=bar
$ echo ${MYMAP[foo]}
bar
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;declare -A MYMAP&lt;&#x2F;span&gt;    # Re-declaring DOES NOT clear an associative array
$ echo ${MYMAP[foo]}
bar
$ unset MYMAP         # You need to unset and re-declare to get a cleared associative array
$ declare -A MYMAP
$ echo ${MYMAP[foo]}

&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;deleting-keys&quot;&gt;Deleting keys&lt;&#x2F;h3&gt;
&lt;pre&gt;$ MYMAP[foo]=bar
$ echo ${MYMAP[foo]}
bar
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;unset ${MYMAP[foo]}&lt;&#x2F;span&gt; # WRONG
$ echo ${MYMAP[foo]}
bar
$ unset MYMAP[foo]    # To delete from an associative array, use &quot;unset&quot; with similar syntax to assigning
$                     # BUT see next section if key contains spaces
$ echo ${MYMAP[foo]}

$ MYMAP[baz]=quux
$ echo ${MYMAP[baz]}
quux
$ K=baz
$ unset MYMAP[$K]       # Can unset using a variable for the key too
$                       # BUT see next section if variable may contain spaces - always better to quote
$ echo ${MYMAP[baz]}

&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;deleting-keys-containing-spaces&quot;&gt;Deleting keys containing spaces&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP
$ MYMAP[foo Z]=bar
$ echo ${MYMAP[foo Z]}
bar
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;unset MYMAP[foo Z]&lt;&#x2F;span&gt;     # WRONG
bash: unset: `MYMAP[foo&#x27;: not a valid identifier
bash: unset: `Z]&#x27;: not a valid identifier
$ unset MYMAP[&quot;foo Z&quot;]    # You must quote keys containing spaces when you unset in an associative array
$ echo ${MYMAP[foo Z]}

$ MYMAP[foo Z]=bar
$ unset MYMAP[&#x27;foo Z&#x27;]    # Single quotes work too
$ echo ${MYMAP[foo Z]}

$ MYMAP[baz A]=quux
$ echo ${MYMAP[baz A]}
quux
$ K=&quot;baz A&quot;
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;unset MYMAP[$K]&lt;&#x2F;span&gt;         # WRONG
bash: unset: `MYMAP[baz&#x27;: not a valid identifier
bash: unset: `A]&#x27;: not a valid identifier
$ unset MYMAP[&quot;$K&quot;]       # You must quote variables whose values may contain spaces when you unset in an associative array
$ echo ${MYMAP[baz A]}

$ MYMAP[baz A]=quux
$ unset MYMAP[&#x27;$K&#x27;]       # Curiously, single quotes work here too, although I&#x27;d suggest avoiding them
$ echo ${MYMAP[baz A]}

&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;length&quot;&gt;Length&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP=( [foo a]=bar [baz b]=quux )
$ echo ${#MYMAP[@]}  # Number of keys in an associative array
2
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo $#MYMAP[@]&lt;&#x2F;span&gt;  # WRONG
0MYMAP[@]
$ &lt;span style=&quot;text-decoration: line-through;&quot;&gt;echo ${#MYMAP}&lt;&#x2F;span&gt;   # WRONG
0
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;numeric-indexing&quot;&gt;Numeric indexing&lt;&#x2F;h3&gt;
&lt;pre&gt;$ declare -A MYMAP=( [foo a]=bar [baz b]=quux )
$ KEYS=(&quot;${!MYMAP[@]}&quot;)      # Make a normal array containing all the keys in the associative array
$ echo ${KEYS[0]}            # Find a key via an index
foo a
$ echo ${MYMAP[${KEYS[0]}]}  # Find a value via an index
bar

$ # Loop through using an index
$ for (( I=0; $I &amp;lt; ${#MYMAP[@]}; I+=1 )); do KEY=${KEYS[$I]};  echo $KEY --- ${MYMAP[$KEY]}; done
foo a --- bar
baz b --- quux
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;scope&quot;&gt;Scope&lt;&#x2F;h3&gt;
&lt;pre&gt;$ unset MYMAP
$ function createmap() { MYMAP[foo]=bar; }  # Implicit creation puts it in the global scope
$ echo ${MYMAP[foo]}

$ createmap
$ echo ${MYMAP[foo]}
bar
&lt;&#x2F;pre&gt;&lt;pre&gt;$ unset MYMAP
&lt;p&gt;$ function createmaplocal() { declare -A MYMAP; MYMAP[foo]=bar; }  # Explicit creation puts it in the local scope
$ echo ${MYMAP[foo]}&lt;&#x2F;p&gt;
&lt;p&gt;$ createmaplocal
$ echo ${MYMAP[foo]}&lt;&#x2F;p&gt;
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;checking-bash-version&quot;&gt;Checking Bash version&lt;&#x2F;h3&gt;
&lt;pre&gt;$ bash --version   # Must be at least version 4 to have associative arrays
GNU bash, version 4.2.24(1)-release (x86_64-pc-linux-gnu)
...
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;bash&#x2F;manual&#x2F;html_node&#x2F;Arrays.html&quot;&gt;Bash manual: Arrays&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.linuxjournal.com&#x2F;content&#x2F;bash-associative-arrays&quot;&gt;Linux Journal: Associative Arrays in Bash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;wiki.bash-hackers.org&#x2F;syntax&#x2F;arrays&quot;&gt;Bash Hackers Wiki: Arrays&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;superuser.com&#x2F;questions&#x2F;195598&#x2F;test-if-element-is-in-array-in-bash&quot;&gt;Superuser: Test if element is in array in Bash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;3112687&#x2F;how-to-iterate-over-associative-array-in-bash&quot;&gt;Stackoverflow: How to iterate over associative array in bash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Walter
    &lt;span class=&quot;comment_date&quot;&gt;2015-06-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wow, just have learned how to use associative arrays, via this very handy page! A clear HowTo.&lt;br&#x2F;&gt;
And what I also especially like about it, is that along with examples how to do things, it also gives the examples how to NOT do certain things. It caught me before falling into a few pitfalls: you have predictive mind. :–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mark Allan
    &lt;span class=&quot;comment_date&quot;&gt;2015-10-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great site… but I am looking for an explanation of the code below?&lt;br&#x2F;&gt;
HOW DOES THIS WORK WITHOUT AN ASSIGN??? I am totally confused, it works, it inits and declares, it’s simple you can see the values but well…  it’s like an awk 1 to me??? Except I can’t see the syntax in any manual or search I’ve done.&lt;&#x2F;p&gt;
&lt;p&gt;echo 1 | awk ‘{   sorex[&quot;W&quot;]&lt;br&#x2F;&gt;
sorex[&quot;B&quot;]&lt;br&#x2F;&gt;
sorex[&quot;TH&quot;]&lt;br&#x2F;&gt;
sorex[&quot;FR&quot;]&lt;br&#x2F;&gt;
for (i in sorex) print i }’&lt;&#x2F;p&gt;
&lt;p&gt;Many thanks in case you can explain it.&lt;&#x2F;p&gt;
&lt;p&gt;Mark (Manchester)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-10-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Mark, that code inside the single quotes is all Awk code, not bash.  This might help: &lt;a href=&quot;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;gawk&#x2F;manual&#x2F;gawk.html&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;gawk&#x2F;manual&#x2F;gawk.html&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    coderofsalvation
    &lt;span class=&quot;comment_date&quot;&gt;2016-02-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Use this higher order function to prevent the pyramid of doom:&lt;&#x2F;p&gt;
&lt;p&gt;foreach(){&lt;br&#x2F;&gt;
arr=&quot;$(declare -p $1)&quot; ; eval &quot;declare -A f=&quot;${arr#*=};&lt;br&#x2F;&gt;
for i in ${!f[@]}; do $2 &quot;$i&quot; &quot;${f[$i]}&quot;; done&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;example:&lt;&#x2F;p&gt;
&lt;p&gt;$ bar(){ echo &quot;$1 -&amp;gt; $2&quot;; }&lt;br&#x2F;&gt;
$ declare -A foo[&quot;flap&quot;]=&quot;three four&quot; foo[&quot;flop&quot;]=&quot;one two&quot;&lt;br&#x2F;&gt;
$ foreach foo bar&lt;br&#x2F;&gt;
flap -&amp;gt; three four&lt;br&#x2F;&gt;
flop -&amp;gt; one two&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bash association arrays | Jacek Kowalczyk MyBlog
    &lt;span class=&quot;comment_date&quot;&gt;2016-02-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] I found a very good article about bash association arrays. Here is a link to it : &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10&#x2F;17&#x2F;bash-associative-array-examples&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10&#x2F;...&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Craig Strickland
    &lt;span class=&quot;comment_date&quot;&gt;2013-07-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;For the benefit of future visitors to this page (like me) that are running pre-4.2 bash, the comment in your statement:&lt;&#x2F;p&gt;
&lt;p&gt;&quot;$ MYMAP[foo]=bar       # Or this line implicitly makes it an associative array (in global scope)&quot;&lt;&#x2F;p&gt;
&lt;p&gt;is not true for bash versions &amp;lt;4.2 wherein associative arrays MUST be explicitly created with &quot;declare -A&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If not pre-declared, then your example (if NOT preceded by &quot;declare -A&quot;):&lt;&#x2F;p&gt;
&lt;p&gt;&quot;$ MYMAP[foo]=bar&quot;&lt;&#x2F;p&gt;
&lt;p&gt;implicitly performs arithmetic evaluation of the expression &quot;foo&quot;, which produces a numeric result of &quot;0&quot;, thereby assigning element &quot;0&quot; of &lt;em&gt;indexed&lt;&#x2F;em&gt; array &quot;MYMAP&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;You can see the result:&lt;&#x2F;p&gt;
&lt;p&gt;$ declare -p MYMAP&lt;br&#x2F;&gt;
declare -a MYMAP=&amp;#39;([0]=&quot;bar&quot;)&amp;#39;&lt;&#x2F;p&gt;
&lt;p&gt;The subscript is &quot;0&quot;, not the string &quot;foo&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;This is in:&lt;&#x2F;p&gt;
&lt;p&gt;$ echo $BASH_VERSION&lt;br&#x2F;&gt;
4.1.5(1)-release&lt;&#x2F;p&gt;
&lt;p&gt;—tgi&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-09-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Craig, thanks for the very informative addition.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dave
    &lt;span class=&quot;comment_date&quot;&gt;2016-09-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’m confused about scope. The following doesn’t work as I expect. Even though I explicitly declare fruit to be an associative array, and it acts like it inside the while loop, the values added during the loop are not present outside the loop. Thanks for any clarification.&lt;&#x2F;p&gt;
&lt;p&gt;$ cat &#x2F;tmp&#x2F;t.bash&lt;br&#x2F;&gt;
#!&#x2F;bin&#x2F;bash&lt;br&#x2F;&gt;
echo &quot;a apple&quot;     &amp;gt;  &#x2F;tmp&#x2F;fruit&lt;br&#x2F;&gt;
echo &quot;b banana&quot;    &amp;gt;&amp;gt; &#x2F;tmp&#x2F;fruit&lt;br&#x2F;&gt;
echo &quot;c cranberry&quot; &amp;gt;&amp;gt; &#x2F;tmp&#x2F;fruit&lt;&#x2F;p&gt;
&lt;p&gt;declare -A fruit&lt;br&#x2F;&gt;
fruit[p]=pumpkin&lt;br&#x2F;&gt;
cat &#x2F;tmp&#x2F;fruit &lt;br&#x2F;&gt;
| while read line; &lt;br&#x2F;&gt;
do &lt;br&#x2F;&gt;
t=$(echo $line|sed -e ’s&#x2F; .&lt;em&gt;&#x2F;&#x2F;‘); &lt;br&#x2F;&gt;
f=$(echo $line|sed -e ’s&#x2F;.&lt;&#x2F;em&gt; &#x2F;&#x2F;’); &lt;br&#x2F;&gt;
fruit[$t]=$f ; &lt;br&#x2F;&gt;
echo &quot;fruit[$t] = ‘${fruit[${t}]}’;  fruit[p]=${fruit[p]}.&quot; ; &lt;br&#x2F;&gt;
done&lt;&#x2F;p&gt;
&lt;p&gt;echo &quot;&quot;&lt;&#x2F;p&gt;
&lt;p&gt;echo &quot;fruit[a]=${fruit[‘a’]}&quot;&lt;br&#x2F;&gt;
echo &quot;fruit[b]=${fruit[‘b’]}&quot;&lt;br&#x2F;&gt;
echo &quot;fruit[c]=${fruit[‘c’]}&quot;&lt;br&#x2F;&gt;
$ &#x2F;tmp&#x2F;t.bash&lt;br&#x2F;&gt;
fruit[a] = ‘apple’;  fruit[p]=pumpkin.&lt;br&#x2F;&gt;
fruit[b] = ‘banana’;  fruit[p]=pumpkin.&lt;br&#x2F;&gt;
fruit[c] = ‘cranberry’;  fruit[p]=pumpkin.&lt;&#x2F;p&gt;
&lt;p&gt;fruit[a]=&lt;br&#x2F;&gt;
fruit[b]=&lt;br&#x2F;&gt;
fruit[c]=&lt;br&#x2F;&gt;
fruit[p]=pumpkin&lt;br&#x2F;&gt;
$ bash —version&lt;br&#x2F;&gt;
GNU bash, version 4.3.11(1)-release (x86_64-pc-linux-gnu)&lt;br&#x2F;&gt;
Copyright © 2013 Free Software Foundation, Inc.&lt;br&#x2F;&gt;
License GPLv3+: GNU GPL version 3 or later&lt;&#x2F;p&gt;
&lt;p&gt;This is free software; you are free to change and redistribute it.&lt;br&#x2F;&gt;
There is NO WARRANTY, to the extent permitted by law.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-09-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Dave, if you set a variable value inside the do .. done it does not leak out of the scope:&lt;&#x2F;p&gt;
&lt;p&gt;$ cat &#x2F;tmp&#x2F;t.bash&lt;br&#x2F;&gt;
#!&#x2F;bin&#x2F;bash&lt;br&#x2F;&gt;
echo &quot;a apple&quot; &amp;gt; &#x2F;tmp&#x2F;fruit&lt;br&#x2F;&gt;
x=2&lt;br&#x2F;&gt;
cat &#x2F;tmp&#x2F;fruit | while read line; do x=3; done&lt;br&#x2F;&gt;
echo $x&lt;&#x2F;p&gt;
&lt;p&gt;$ &#x2F;tmp&#x2F;t.bash&lt;br&#x2F;&gt;
2&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    daveadams
    &lt;span class=&quot;comment_date&quot;&gt;2016-09-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;In order to get the scope to work how you expect, @Dave, you need to invert the operations. It doesn’t work because you are piping the output of &lt;code&gt;cat &#x2F;tmp&#x2F;fruit&lt;&#x2F;code&gt; into a while loop, but the second command in the pipe (the while loop) spawns in a new process. So in that subprocess, the variables are being set, but when the while loop terminates the subprocess terminates and the changes to the variables are lost. So in order to do what you want, the while loop needs to be in the process with the rest of the script. So, instead you can do:&lt;&#x2F;p&gt;
&lt;p&gt;$ cat test.sh&lt;br&#x2F;&gt;
#!&#x2F;bin&#x2F;bash&lt;&#x2F;p&gt;
&lt;p&gt;cat &amp;gt;&#x2F;tmp&#x2F;fruit &amp;lt;
a apple&lt;br&#x2F;&gt;
b banana&lt;br&#x2F;&gt;
c cranberry&lt;br&#x2F;&gt;
FRUITS&lt;&#x2F;p&gt;
&lt;p&gt;declare -A fruit=( [p]=pumpkin )&lt;&#x2F;p&gt;
&lt;p&gt;while read t f; do&lt;br&#x2F;&gt;
fruit[$t]=&quot;$f&quot;&lt;br&#x2F;&gt;
echo &quot;fruit[$t] = &amp;#39;${fruit[${t}]}&amp;#39;; fruit[p]=${fruit[p]}.&quot;&lt;br&#x2F;&gt;
done &amp;lt; &#x2F;tmp&#x2F;fruit&lt;&#x2F;p&gt;
&lt;p&gt;echo &quot;&quot;&lt;br&#x2F;&gt;
for i in &quot;${!fruit[@]}&quot;; do&lt;br&#x2F;&gt;
echo &quot;fruit[$i] = &amp;#39;${fruit[$i]}&amp;#39;&quot;&lt;br&#x2F;&gt;
done&lt;&#x2F;p&gt;
&lt;p&gt;$ bash test.sh&lt;br&#x2F;&gt;
fruit[a] = &amp;#39;apple&amp;#39;; fruit[p]=pumpkin.&lt;br&#x2F;&gt;
fruit[b] = &amp;#39;banana&amp;#39;; fruit[p]=pumpkin.&lt;br&#x2F;&gt;
fruit[c] = &amp;#39;cranberry&amp;#39;; fruit[p]=pumpkin.&lt;&#x2F;p&gt;
&lt;p&gt;fruit[a] = &amp;#39;apple&amp;#39;&lt;br&#x2F;&gt;
fruit[b] = &amp;#39;banana&amp;#39;&lt;br&#x2F;&gt;
fruit[c] = &amp;#39;cranberry&amp;#39;&lt;br&#x2F;&gt;
fruit[p] = &amp;#39;pumpkin&amp;#39;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-09-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Excellent, thanks Dave!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    mop
    &lt;span class=&quot;comment_date&quot;&gt;2013-11-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Passing to a function and how to assign between variables (both arrays) is missing IMHO.&lt;&#x2F;p&gt;
&lt;p&gt;Regards, and thank you, Matteo.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-12-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Matteo, thanks – yes those would be useful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Harro Merkus
    &lt;span class=&quot;comment_date&quot;&gt;2013-12-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for your post.&lt;&#x2F;p&gt;
&lt;p&gt;I was looking for a way to delete a variable key from an associative array, where that variable may be a single space.&lt;br&#x2F;&gt;
Then these do not work:&lt;br&#x2F;&gt;
unset MYMAP[ ]&lt;br&#x2F;&gt;
unset MYMAP[’ ‘]&lt;br&#x2F;&gt;
unset MYMAP[&quot; &quot;]&lt;br&#x2F;&gt;
K=’ ‘&lt;br&#x2F;&gt;
unset MYMAP[$K]&lt;br&#x2F;&gt;
unset MYMAP[&quot;$K&quot;]&lt;&#x2F;p&gt;
&lt;p&gt;However, this one does work:&lt;br&#x2F;&gt;
unset MYMAP[’$K’]&lt;&#x2F;p&gt;
&lt;p&gt;Also, if K is a single or double quote, only the latter one works!&lt;&#x2F;p&gt;
&lt;p&gt;Using GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-01-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Harro, thanks – useful information.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sharon
    &lt;span class=&quot;comment_date&quot;&gt;2017-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great post!!! Thanks a lot,&lt;&#x2F;p&gt;
&lt;p&gt;Can you please explain why do you add &quot;+_&quot; when you trying to test value existing?&lt;br&#x2F;&gt;
It works for me without this addition:&lt;br&#x2F;&gt;
&#x2F;home&#x2F;ubuntu# if [ ${MYMAP[blablabla]} ]; then echo yes; else echo no;fi&lt;&#x2F;p&gt;
&lt;p&gt;no&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Sharon, I don’t actually know why I added +_ – I am wondering whether this is an artefact of copying and pasting from somewhere else… Thanks for the comment!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Greywolf
    &lt;span class=&quot;comment_date&quot;&gt;2014-01-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Default variable test&#x2F;expansion rules apply:&lt;&#x2F;p&gt;
&lt;p&gt;$ declare -A ax;&lt;br&#x2F;&gt;
$ ax[foo]=&quot;xkcd&quot;;&lt;br&#x2F;&gt;
$ echo ${ax[foo]:-MISSING};&lt;br&#x2F;&gt;
xkcd&lt;br&#x2F;&gt;
$ echo ${ax[bar]:-MISSING};&lt;br&#x2F;&gt;
MISSING&lt;br&#x2F;&gt;
$ echo ${ax[foo]:+SET};&lt;br&#x2F;&gt;
SET&lt;br&#x2F;&gt;
$&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-01-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Greywolf, very helpful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sameer Oak
    &lt;span class=&quot;comment_date&quot;&gt;2014-02-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you very much for such a priceless post.&lt;&#x2F;p&gt;
&lt;p&gt;At present, I’m struggling to find solution to either of the following problems:&lt;br&#x2F;&gt;
1&amp;gt; how to convert a nornal array (indexed array with index starting at 0) into an associative array where value becomes a key and value itself is the value.&lt;br&#x2F;&gt;
2&amp;gt; Create a new assoc array from indexed array where values are keys. And this in a single statement. I know it can very well be done using a loop but for a huge sized array containing almost 500,000 elements,&lt;br&#x2F;&gt;
a loop is an overhead.&lt;br&#x2F;&gt;
3&amp;gt; Create an assoc array from the result of sql query. I normally create an indexed array from the sql query result as below:&lt;br&#x2F;&gt;
mapfile -t a_dummy &amp;lt;&amp;lt;&amp;lt; &quot;$(mysql -u root —disable-column-names —silent -B -e &quot;select * from dummy_tbl;&quot; &quot;$DB_NAME&quot;)&quot;&lt;br&#x2F;&gt;
where $DB_NAME is the variable pointing to DB name string.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you very much in advance.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    just_an_abstraction
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Really useful, I was stuck declaring an associative implicitly inside a function, apparently you need declare -A for that to work fine. And it apparently stays in local scope too.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2017-05-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, glad it was useful!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    CPRitter
    &lt;span class=&quot;comment_date&quot;&gt;2014-03-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy:&lt;br&#x2F;&gt;
Thanks for the informative write-up!  I wish I had found it before I spent an hour figuring it out myself.  Now, I was brought to your site while searching for a solution to this …&lt;&#x2F;p&gt;
&lt;p&gt;Is there a less clumsy method of sorting keys than this (spaces in keys must be preserverd)…&lt;&#x2F;p&gt;
&lt;p&gt;bash-4.1$ declare -A ARY=( [fribble]=frabble [grabble]=gribble [co bb le]=cribble [babble]=bibble [zibble]=zabble [n o bbl e]=nibble [mobble]=mibble )&lt;br&#x2F;&gt;
bash-4.1$ keys=( ${!ARY[@]} )&lt;br&#x2F;&gt;
bash-4.1$ IFS=$‘\n’ sorted_keys=( $( echo -e &quot;${keys[@]&#x2F;%&#x2F;\n}&quot; | sed -r -e ’s&#x2F;^ *&#x2F;&#x2F;’ -e ‘&#x2F;^$&#x2F;d’ | sort ) )&lt;br&#x2F;&gt;
bash-4.1$ for key in  &quot;${sorted_keys[@]}&quot;; do echo &quot;$key: ${ARY[$key]}&quot;; done&lt;br&#x2F;&gt;
babble: bibble&lt;br&#x2F;&gt;
co bb le: cribble&lt;br&#x2F;&gt;
fribble: frabble&lt;br&#x2F;&gt;
grabble: gribble&lt;br&#x2F;&gt;
mobble: mibble&lt;br&#x2F;&gt;
n o bbl e: nibble&lt;br&#x2F;&gt;
zibble: zabble&lt;br&#x2F;&gt;
bash-4.1$&lt;&#x2F;p&gt;
&lt;p&gt;Thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-03-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi CPRitter, that looks like a pretty good way to do this, but I think at this point I’d be reaching for Perl or Python…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mac OS X Bash – upgrade – Open Source Refinery
    &lt;span class=&quot;comment_date&quot;&gt;2017-06-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Bash associative array examples […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Doron
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;@Sharon, Andy,&lt;&#x2F;p&gt;
&lt;p&gt;Re Missing Keys and the &quot;+_&quot; in the examples: this is in fact quite important, and it’s a good thing you quoted it in this guide.&lt;&#x2F;p&gt;
&lt;p&gt;It differentiates between the case where a key does not exist, and the case where it does exist but its value is null.&lt;&#x2F;p&gt;
&lt;p&gt;To use Sharon’s example, this indeed seems to work:&lt;&#x2F;p&gt;
&lt;p&gt;if [ ${MYMAP[blablabla]} ]; then echo yes; else echo no;fi&lt;&#x2F;p&gt;
&lt;p&gt;no&lt;&#x2F;p&gt;
&lt;p&gt;BUT if you&lt;&#x2F;p&gt;
&lt;p&gt;MYMAP[blablabla]=&quot;&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Then&lt;&#x2F;p&gt;
&lt;p&gt;if [ ${MYMAP[blablabla]} ]; then echo yes; else echo no;fi&lt;&#x2F;p&gt;
&lt;p&gt;no&lt;&#x2F;p&gt;
&lt;p&gt;But&lt;&#x2F;p&gt;
&lt;p&gt;if [ ${MYMAP[blablabla]+_} ]; then echo yes; else echo no;fi&lt;&#x2F;p&gt;
&lt;p&gt;yes&lt;&#x2F;p&gt;
&lt;p&gt;Et voila.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Rahul
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Nice Way to show examples.&lt;br&#x2F;&gt;
Other examples of Array Basics Shell Script:&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;blog.prakhar.info&#x2F;array-basics-shell-script&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;blog.prakhar.info&#x2F;array-basics-shell-...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Rahul!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-01-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Doron!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Avi
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;declare -A MYMAP doesn’t work and throws an error:&lt;br&#x2F;&gt;
declare: -A: invalid option&lt;br&#x2F;&gt;
declare: usage: declare [-afFirtx] [-p] [name[=value] …]&lt;&#x2F;p&gt;
&lt;p&gt;using the quotes around the values throws an error like this:&lt;br&#x2F;&gt;
array[wow]: command not found&lt;br&#x2F;&gt;
san francisco&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-12-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Avi, are you sure you are using bash?  I just tried declare -A MYMAP here and it worked.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Will
    &lt;span class=&quot;comment_date&quot;&gt;2014-05-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’m jealous of this. Thanks for the write up but would you consider wrapping &quot;bash version 4 only&quot; at the start of the article in strong tags? :) I just bashed (cough) my head against the keyboard for 10 minutes because I’m on bash 3.2.8 (OSX 10.7.5).&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    david
    &lt;span class=&quot;comment_date&quot;&gt;2019-06-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Will people ever learn that&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;bash --version
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;is not the way to check the version of your current bash? You’re only checking the version of the bash which is found first in your path, not necessarily the one you’re currently running. You can and should use&lt;&#x2F;p&gt;
&lt;pre class=&quot;chroma&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span class=&quot;cl&quot;&gt;echo $BASH_VERSION
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;or just the keyboard shortcut C-x C-v.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2019-06-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks david, good point.  I would prefer it phrased less rudely though.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    david
    &lt;span class=&quot;comment_date&quot;&gt;2019-06-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Maybe, but in these attention dearth times formulating in sharply pointed way is often the only way to get people notice and remember. I found the rest of the article quite good, so it was a disappointment to see this tip at the end. As you can guess it was not the first time I saw it, but in an article like this, people will copy it, as you can also see in the comments above.&lt;&#x2F;p&gt;
&lt;p&gt;The problem with such tips is that they will give the right answer most of the time, leading to even more confusion and frustration when they don’t.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Akito
    &lt;span class=&quot;comment_date&quot;&gt;2020-03-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Amazing! Explains everything about associative arrays in a single article. Answered all my questions at once.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-03-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Akito, glad it helped!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Self-Perfection
    &lt;span class=&quot;comment_date&quot;&gt;2020-07-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;There is an error in &quot;Numeric indexing&quot; section&lt;br&#x2F;&gt;
Example&lt;br&#x2F;&gt;
KEYS=(${!MYMAP[@]})&lt;&#x2F;p&gt;
&lt;p&gt;Should actually be&lt;br&#x2F;&gt;
KEYS=(&quot;${!MYMAP[@]}&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;otherwise keys with spaces would split to separate array items.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-07-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Awesome, thank you Self-Perfection – I have fixed it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-05-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Will, updated.  Sorry you can’t use it!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    R Sriram
    &lt;span class=&quot;comment_date&quot;&gt;2020-12-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks a million for the page and examples. It’s been a L.O.N.G time since I went to the net for ‘just bash’ questions  (:=), so it was great to hear that bash now has ass.arrays.   I used to do  a lot of bash+cmdline-perl (perl -e) to do what a simple ass.array in bash could have done.   Now, I have my task cut out.&lt;br&#x2F;&gt;
The way you have written the examples is just as one developer talks to another..  Simple, neat, to the point.&lt;br&#x2F;&gt;
Thanks again.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    BC
    &lt;span class=&quot;comment_date&quot;&gt;2020-12-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Nice article – thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    bash dynamic (variable) variable names
    &lt;span class=&quot;comment_date&quot;&gt;2022-06-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] [VERY GOOD TUTORIAL!] *****&lt;a href=&quot;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10&#x2F;17&#x2F;bash-associative-array-examples&#x2F;&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10...&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Associative Arrays in Shell Scripts
    &lt;span class=&quot;comment_date&quot;&gt;2022-06-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] I found a nice, short tutorial on this here. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    osirisgothra
    &lt;span class=&quot;comment_date&quot;&gt;2014-10-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Its good to note that:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt;item=( [item1]=&quot;one&quot; [item2]=&quot;two )&lt;&#x2F;p&gt;
&lt;p&gt;if done on a un[define]d variable, will treat it like an -a instead of an -A, which causes the last entry only to be recognized as the first indexer (zero) unless, of course, those items have value,&lt;&#x2F;p&gt;
&lt;p&gt;in the above example, if the variables $item1 and $item2 are un[define]d, then the result would be:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt; declare -p item&lt;br&#x2F;&gt;
item=([0]=&quot;two&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;this happened because undeclared variables have an implicit value of 0 when used as an indexer, it would be so these two lines are identical:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt;item=( [item1]=&quot;one&quot; [item2]=&quot;two )&lt;br&#x2F;&gt;
&amp;gt;declare -p item&lt;br&#x2F;&gt;
item=([0]=&quot;two&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt;item=( [0]=&quot;one&quot; [0]=&quot;two )&lt;br&#x2F;&gt;
&amp;gt;declare -p item&lt;br&#x2F;&gt;
item=([0]=&quot;two&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;As you can see on the second line, the index ‘0’ gets defined twice, of course the last being the final value for that index.&lt;&#x2F;p&gt;
&lt;p&gt;The case is quite different if you have defined values for $item1 and $item2:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt;item1=12&lt;br&#x2F;&gt;
&amp;gt;item2=24&lt;br&#x2F;&gt;
&amp;gt;item=( [item1]=&quot;one&quot; [item2]=&quot;two )&lt;&#x2F;p&gt;
&lt;p&gt;then you get this:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt; declare -p item&lt;br&#x2F;&gt;
item=( [12]=&quot;one&quot; [24]=&quot;two )&lt;&#x2F;p&gt;
&lt;p&gt;and verify it like this:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt;echo ${item[12]}&lt;br&#x2F;&gt;
one&lt;br&#x2F;&gt;
&amp;gt;echo ${item[24]}&lt;br&#x2F;&gt;
two&lt;&#x2F;p&gt;
&lt;p&gt;Of course, if you had already had values in the other index 0, it would have been erased by this though not touching index 0 you are still resetting the value of the variable —– unless you used += instead of =.  Furthermore, if the values of $item1 and $item2 were not integers (strings), the values would go back to being implicitly 0 again.&lt;&#x2F;p&gt;
&lt;p&gt;This is important because many programmers expect that because integer arrays are implicit, that the associative arrays &lt;em&gt;should be&lt;&#x2F;em&gt; too. And it even appears that way if the array was [declare]d one previously.&lt;&#x2F;p&gt;
&lt;p&gt;As a RULE, it is good to just declare ALL variables. I make it a habit to use &quot;shopt -o  -s nounset&quot; in my scripts.  However, interactive scripts like .bashrc or completion scripts do not always have this luxury, because it’s a pain to set it, and then unset it, also saving the value which is overhead in the sense of time taken to implement&#x2F;reimplement each time. In those cases, hopefully the habit of doing it in scripts rubs off on you enough to have it done in the interactive ones as well :)&lt;&#x2F;p&gt;
&lt;p&gt;Hope that helped (someone) this font is so small i can hardly read it for some reason today, so if i made a mistake that’s why ( too lazy to zoom :) ) &amp;lt;– double chin!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-10-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks osirisgothra, very helpful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    qwe
    &lt;span class=&quot;comment_date&quot;&gt;2015-01-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;great article!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Running Dojo 1.7+ DOH unit tests on the command line with Rhino</title>
          <pubDate>Tue, 09 Oct 2012 16:19:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/10/09/running-dojo-doh-unit-tests-on-the-command-line-with-rhino/</link>
          <guid>https://artificialworlds.net/blog/2012/10/09/running-dojo-doh-unit-tests-on-the-command-line-with-rhino/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/10/09/running-dojo-doh-unit-tests-on-the-command-line-with-rhino/">&lt;p&gt;To run your own &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;dojotoolkit.org&#x2F;reference-guide&#x2F;util&#x2F;doh.html&quot;&gt;DOH&lt;&#x2F;a&gt;-based unit tests on the command line using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Rhino&quot;&gt;Rhino&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;NOTE: this is Dojo 1.7 and above. For 1.6, there was a whole other cryptic incantation.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;project-layout&quot;&gt;Project layout&lt;&#x2F;h3&gt;
&lt;p&gt;Imagine your code is somewhere different from dojo, and another library you use is somewhere else:&lt;&#x2F;p&gt;
&lt;pre&gt;C:&#x2F;code&#x2F;mycode&#x2F;org&#x2F;me&#x2F;mytests&#x2F;
                             ...
                             mytestmodule.js
                             ...
C:&#x2F;code&#x2F;mycode&#x2F;org&#x2F;them&#x2F;nicelib&#x2F;
                             ...
C:&#x2F;libs&#x2F;dojo&#x2F;dojo&#x2F;
                 ...
                 dojo.js
                 ...
             dijit&#x2F;
                 ...
             dojox&#x2F;
                 ...
             util&#x2F;doh&#x2F;
                     ...
                     main.js
                     ...
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;config-file&quot;&gt;Config file&lt;&#x2F;h3&gt;
&lt;p&gt;Yes, you need a config file. Imagine it&#x27;s at C:&#x2F;code&#x2F;mycode&#x2F;dohconfig.js and it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;require({
    paths: {
        &quot;org&#x2F;me&quot; : &quot;..&#x2F;..&#x2F;..&#x2F;code&#x2F;mycode&#x2F;org&#x2F;me&quot;,
        &quot;org&#x2F;them&quot; : &quot;..&#x2F;..&#x2F;..&#x2F;code&#x2F;mycode&#x2F;org&#x2F;them&#x2F;nicelib&quot;
    }
});
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;command-line&quot;&gt;Command line&lt;&#x2F;h3&gt;
&lt;p&gt;Now you can run your tests like this:&lt;&#x2F;p&gt;
&lt;pre&gt;java -jar C:&#x2F;libs&#x2F;dojo&#x2F;util&#x2F;shrinksafe&#x2F;js.jar C:&#x2F;libs&#x2F;dojo&#x2F;dojo&#x2F;dojo.js baseUrl=file:&#x2F;&#x2F;&#x2F;C:&#x2F;libs&#x2F;dojo&#x2F;dojo load=file:&#x2F;&#x2F;&#x2F;C:&#x2F;code&#x2F;mycode&#x2F;dohconfig.js load=doh test=org&#x2F;me&#x2F;mytests&#x2F;mytestmodule
&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;explanation&quot;&gt;Explanation&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;java -jar - run Java and execute a JAR file.&lt;&#x2F;li&gt;
&lt;li&gt;C:&#x2F;libs&#x2F;dojo&#x2F;util&#x2F;shrinksafe&#x2F;js.jar - path to the JAR file that is Rhino, a JavaScript interpreter written in Java (and included in Dojo&#x27;s source distribution).&lt;&#x2F;li&gt;
&lt;li&gt;C:&#x2F;libs&#x2F;dojo&#x2F;dojo&#x2F;dojo.js - the Dojo &quot;loader&quot; - unlike in 1.6 and earlier, you don&#x27;t run DOH&#x27;s runner.js. Instead you run dojo.js and pass &quot;load=doh&quot; as an argument.&lt;&#x2F;li&gt;
&lt;li&gt;baseUrl=file:&#x2F;&#x2F;&#x2F;C:&#x2F;libs&#x2F;dojo&#x2F;dojo - a URL form of the location of the directory that contains dojo.js.&lt;&#x2F;li&gt;
&lt;li&gt;load=file:&#x2F;&#x2F;&#x2F;C:&#x2F;code&#x2F;mycode&#x2F;dohconfig.js - the location of your config file, which defines the &quot;paths&quot; variable, previously (in 1.6) known as registerModulePaths. This variable helps Dojo find your code based on its module name (here &quot;org&#x2F;me&quot;).&lt;&#x2F;li&gt;
&lt;li&gt;load=doh - after you&#x27;ve read (actually, executed) the config file, execute DOH.&lt;&#x2F;li&gt;
&lt;li&gt;test=org&#x2F;me&#x2F;mytests&#x2F;mytestmodule - the module name of your test (not the path - a module name which can be found using the lookups in the paths defined in your config file).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    rajesh
    &lt;span class=&quot;comment_date&quot;&gt;2013-04-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;br&#x2F;&gt;
Is it possible to load the dojo into the java application with the help of Rhino adn envjs?&lt;br&#x2F;&gt;
i’m in a need to render the dojo charts on server side. when trying to load the dojo into java via rhino, an Exception rises which is as follows,&lt;br&#x2F;&gt;
&quot;org.mozilla.javascript.EcmaError: TypeError: Cannot read property &quot;parentNode&quot; from undefined &quot;&lt;br&#x2F;&gt;
Im struggling to find a way to load Dojo. Please help me&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-04-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi rajesh, I’d be surprised if that worked.  I suggest using a hidden web browser control?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    m
    &lt;span class=&quot;comment_date&quot;&gt;2012-10-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Excellent article…is there a way to read the unit test results from the command line and log them to a text&#x2F;xml file? Thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-10-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks!  To log the output into a text file, just add &quot; &amp;gt; log.txt&quot; at the end of the command (or is that not what you meant?)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ricardo Lucio
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Excellent article!!&lt;&#x2F;p&gt;
&lt;p&gt;One question: when I try to run a test that touch the dom (e.g.: when I create a dojox.mobile.Button in my test) it complains that window is undefined.&lt;br&#x2F;&gt;
After some tests I noticed that the rhino engine didn’t provide a dom implementation.&lt;br&#x2F;&gt;
Am I missing someting?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Ricardo, thanks!  You are not missing something.  There is no DOM when running in Rhino.  env.js (&lt;a href=&quot;http:&#x2F;&#x2F;www.envjs.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.envjs.com&#x2F;&lt;&#x2F;a&gt;) might do what you need, if you want to do tests of DOM-manipulating code on the command line.  There’s still a lot you can’t do without a real-live browser, though.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Lambda functions timeline</title>
          <pubDate>Fri, 14 Sep 2012 13:34:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/09/14/lambda-functions-timeline/</link>
          <guid>https://artificialworlds.net/blog/2012/09/14/lambda-functions-timeline/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/09/14/lambda-functions-timeline/">&lt;p&gt;I did a talk at work about lambda functions (anonymous functions), and something possessed me to make a timeline of when they were introduced into various languages. Some languages were born with them, and some grew them later - in the latter case I give the language version and date in which they were introduced.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s probably entirely wrong, but here it is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;timeline.svg&quot;&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;timeline.svg&quot; alt=&quot;Dates when lambda functions were introduced into various programming languages&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Note that C# had varying levels of support for lambda functions in different versions. I used the version (3.0) in which Wikipedia describes its support as &quot;full&quot;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Goodness in programming languages, part 3 &quot; not doing the same thing more than once</title>
          <pubDate>Wed, 05 Sep 2012 07:36:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/09/05/goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once/</link>
          <guid>https://artificialworlds.net/blog/2012/09/05/goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/09/05/goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once/">&lt;p&gt;&lt;em&gt;Posts in this series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;02&#x2F;10&#x2F;goodness-in-programming-languages-part-1-syntax-and-layout&#x2F;&quot;&gt;Syntax&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;03&#x2F;08&#x2F;goodness-in-programming-languages-part-2-getting-your-code-running&#x2F;&quot;&gt;Deployment&lt;&#x2F;a&gt;, Metaprogramming, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;01&#x2F;goodness-in-programming-languages-part-4-ownership-memory&#x2F;&quot;&gt;Ownership&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to use a word here - don&#x27;t stop reading: &lt;strong&gt;Metaprogramming&lt;&#x2F;strong&gt;. Does the language provide what you need to avoid repeating yourself?&lt;&#x2F;p&gt;
&lt;p&gt;Repeating boilerplate code, algorithms and most importantly ideas, slows you down, makes maintenance difficult, and allows all kinds of mistakes to creep in. If a language provides the ability to abstract, name and re-use all the different types of structure it contains, you can avoid harmful repetition.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some things I like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python, JavaScript and Scheme&#x27;s ability to treat functions like any other object.&lt;&#x2F;strong&gt; A massive step towards sharing code is being allowed to pass around something that can be called without worrying about what it is.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&#x27;s ability to define an algorithm independently of types.&lt;&#x2F;strong&gt; In Scheme, there is never a need to write another version of the same function because it deals with different types of thing.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Python&#x27;s ability to read and modify classes just like any other object.&lt;&#x2F;strong&gt; Want a class just like your current one, except it logs every method call? Write a function that copies and modifies the class definition.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&#x27;s ability write code about code.&lt;&#x2F;strong&gt; In Scheme, code is just some nested lists. It&#x27;s trivial to build and modify code without stepping out of the language.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;C++&#x27;s ability to write code that runs at compile time.&lt;&#x2F;strong&gt; If you can stand the syntax and (lack of) debugging, C++ template metaprogramming allows you to build C++ code at compile time without stepping out of the compiler environment.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scheme and C&#x27;s macro systems.&lt;&#x2F;strong&gt; Both Scheme and C (and C++) allow you to write macros that build commonly-repeated code. Scheme&#x27;s syntax for this is much easier to work with.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Until you&#x27;ve experienced the freedom of totally generic code in a language like Scheme it&#x27;s hard to explain why the &quot;Generics&quot; features of some languages are so lacking. Of course, static typed languages work under different constraints. Would it be possible to write a language with very strong generic programming features, but which still allows static typing and compiling to native, non-generic code? I think so.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tail Call Optimisation in C++ - lightning talk video</title>
          <pubDate>Mon, 30 Jul 2012 08:49:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/07/30/tail-call-optimisation-in-c-lightning-talk-video/</link>
          <guid>https://artificialworlds.net/blog/2012/07/30/tail-call-optimisation-in-c-lightning-talk-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/07/30/tail-call-optimisation-in-c-lightning-talk-video/">&lt;p&gt;You can watch the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;skillsmatter.com&#x2F;podcast&#x2F;agile-testing&#x2F;lightning-talk-andy-balaam-implementing-tail-call-optimisation-in-c-plus-plus&quot;&gt;Tail Call Optimisation in C++ lightning talk video&lt;&#x2F;a&gt;, which I gave at the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;conferences&#x2F;accu_conference_2012&quot;&gt;ACCU 2012 conference&lt;&#x2F;a&gt; in April.&lt;&#x2F;p&gt;
&lt;p&gt;You can also read the (clearer and more correct) writeup I did later: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;Tail Call Optimisation in C++&lt;&#x2F;a&gt; or the subsequent article published in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;c310&#x2F;&quot;&gt;Overload 109&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam’s Blog » Blog Archive » Lighting talk – Tail call optimisation in C++
    &lt;span class=&quot;comment_date&quot;&gt;2012-07-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Update: watch the video […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Cool Raspberry Pi Projects</title>
          <pubDate>Fri, 20 Jul 2012 12:19:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/07/20/cool-raspberry-pi-projects/</link>
          <guid>https://artificialworlds.net/blog/2012/07/20/cool-raspberry-pi-projects/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/07/20/cool-raspberry-pi-projects/">&lt;p&gt;Someone claimed that everyone who has a Raspberry Pi has done what I did with it: try it out then file it in a drawer marked &quot;must play with later&quot;. To try and debunk this claim, here are some cool projects I have seen.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: I made some videos on how to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;10&#x2F;30&#x2F;my-first-raspberry-pi-game-part-01-before-we-star&#x2F;&quot;&gt;write your first ever game on your Raspberry Pi&lt;&#x2F;a&gt;!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberry-pi-accessories-you-already-have&quot;&gt;Raspberry Pi Accessories You Already Have!&lt;&#x2F;h3&gt;
&lt;p&gt;This guy uses an old PSOne as a screen+audio, a phone battery for power, and a hard drive or mp3 player for storage.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=V5bEujnl1Zo&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;V5bEujnl1Zo&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberry-pi-robot-test&quot;&gt;Raspberry Pi Robot Test!&lt;&#x2F;h3&gt;
&lt;p&gt;A robot carrying a RPi on its back, controllable via the Internet. It runs a node.js web server, and receives HTTP requests to control it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=VVNgPjM5pU4&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;VVNgPjM5pU4&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberry-pi-quake-3-demo&quot;&gt;Raspberry Pi - Quake 3 demo&lt;&#x2F;h3&gt;
&lt;p&gt;A playable Quake 3.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=e_mDuJuvZjI&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;e_mDuJuvZjI&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberrypi-xbmc-android-remote-control-demo&quot;&gt;RaspberryPi: XBMC Android Remote Control Demo&lt;&#x2F;h3&gt;
&lt;p&gt;A video player, with a mobile phone as the remote control.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=WrACZN6qoDw&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;WrACZN6qoDw&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pie1-a-raspberry-pi-sends-live-images-from-near-space&quot;&gt;PIE1 -Â– Raspberry Pi Sends Live Images from Near Space&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.daveakerman.com&#x2F;?p=592&quot;&gt;Story&lt;&#x2F;a&gt; | &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;daveake&#x2F;sets&#x2F;72157630584759530&#x2F;&quot;&gt;Photos of launch&lt;&#x2F;a&gt; | &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.flickr.com&#x2F;photos&#x2F;daveake&#x2F;sets&#x2F;72157630589478676&#x2F;&quot;&gt;Photos from space&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Balloon flying up to 40km taking photos using a USB webcam and saving them to an SD card.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;vnc-viewer-on-raspberry-pi&quot;&gt;VNC Viewer on Raspberry Pi&lt;&#x2F;h3&gt;
&lt;p&gt;A VNC viewer allowing you to create a thin client&#x2F;terminal in other rooms of your house.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=HRqSOjtCuVI&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;HRqSOjtCuVI&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Also see the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;rpitc.blogspot.pt&#x2F;&quot;&gt;Raspberry Pi Thin Client project&lt;&#x2F;a&gt;, for a small distro containing VMWare, Remote Desktop, Citrix Receiver &amp;amp; a browser.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberry-pi-running-the-fuse-zx-spectrum-emulator&quot;&gt;Raspberry Pi running the Fuse ZX Spectrum Emulator&lt;&#x2F;h3&gt;
&lt;p&gt;A ZX Spectrum Emulator, taking the nostalgia to the max.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=zkbvDO1K_-c&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;zkbvDO1K_-c&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;make-a-raspberry-pi-web-server&quot;&gt;Make A Raspberry Pi Web Server&lt;&#x2F;h3&gt;
&lt;p&gt;Make your own Â£25 web server.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=WgcNBjIJNYs&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;WgcNBjIJNYs&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;voice-controlled-robot-using-a-raspberry-pi&quot;&gt;Voice Controlled Robot using a Raspberry Pi&lt;&#x2F;h3&gt;
&lt;p&gt;A voice-controlled robot arm.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=_U_v9tRD68k&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;_U_v9tRD68k&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pi-plays-poppins&quot;&gt;Pi Plays Poppins&lt;&#x2F;h3&gt;
&lt;p&gt;A Pi playing the Glockenspeil.&lt;&#x2F;p&gt;
&lt;iframe src=&quot;http:&#x2F;&#x2F;player.vimeo.com&#x2F;video&#x2F;44300050&quot; width=&quot;420&quot; height=&quot;236&quot; frameborder=&quot;0&quot; webkitallowfullscreen=&quot;&quot; mozallowfullscreen=&quot;&quot; allowfullscreen=&quot;&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;vimeo.com&#x2F;44300050&quot;&gt;Pi Plays Poppins&lt;&#x2F;a&gt; from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;vimeo.com&#x2F;grumpymike&quot;&gt;Mike Cook&lt;&#x2F;a&gt; on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;vimeo.com&quot;&gt;Vimeo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;raspberry-tank-first-day-out&quot;&gt;Raspberry Tank - First Day Out&lt;&#x2F;h3&gt;
&lt;p&gt;A tank, currently directly controlled by typing into ssh. They are working on an autonomous version.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=QSPcJL1CGjA&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;QSPcJL1CGjA&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;learn-how-to-program&quot;&gt;Learn how to program&lt;&#x2F;h3&gt;
&lt;p&gt;My own tutorial on how to write computer programs in Python on your Pi. You will make a very simple game.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=glV-LP39__c&quot;&gt;&lt;img src=&quot;https:&#x2F;&#x2F;img.youtube.com&#x2F;vi&#x2F;glV-LP39__c&#x2F;0.jpg&quot; alt=&quot;Embedded YouTube video&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;not-yet-implemented-ideas&quot;&gt;Not-yet-implemented ideas&lt;&#x2F;h3&gt;
&lt;p&gt;There are lots of people talking about ideas for projects. Here are some I&#x27;d like to see come off:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Voice-activated wall-mounted screen&lt;&#x2F;li&gt;
&lt;li&gt;Advanced alarm clock&lt;&#x2F;li&gt;
&lt;li&gt;Motion-activated lights and&#x2F;or camera&lt;&#x2F;li&gt;
&lt;li&gt;Internet radio&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;moccapi.blogspot.com&#x2F;&quot;&gt;Coffee machine&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Phone system&lt;&#x2F;li&gt;
&lt;li&gt;Web browser on your TV&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;fishpi.org&#x2F;&quot;&gt;Autonomous boat&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Scheme 7: Macros video</title>
          <pubDate>Mon, 02 Jul 2012 07:33:08 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/07/02/scheme-7-macros-video/</link>
          <guid>https://artificialworlds.net/blog/2012/07/02/scheme-7-macros-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/07/02/scheme-7-macros-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, Macros.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video explains the ultimate alternative - when nothing else is flexible enough, we can create our own bits of lanugage using macros.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 7: Macros&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;7a1d0247-3984-48b1-9ef3-e1bf290fe455&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-07-macros&#x2F;scheme-07-macros.html&quot;&gt;Slides for Scheme 7: Macros&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jennii
    &lt;span class=&quot;comment_date&quot;&gt;2020-05-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Andy.  I did computer science-y stuff years and years ago.   After other careers, I retired and thought it would be fun to remember.  Your Scheme videos did a wonderful job of nudging my memory.  Thanks ever so much.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2020-06-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Jennii, so encouraged to hear that!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Scheme 6: Lambda video</title>
          <pubDate>Tue, 26 Jun 2012 16:55:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/06/26/scheme-6-lambda-video/</link>
          <guid>https://artificialworlds.net/blog/2012/06/26/scheme-6-lambda-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/06/26/scheme-6-lambda-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, Lambda, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video explains the lambda function, which allows you to define anonymous functions. It goes on to bend your mind with 2 examples of the enormous power of functions and closures in Scheme.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 6: Lambda&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;c810c4c0-baff-4b6a-a557-67fc803d5728&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-06-lambda&#x2F;scheme-06-lambda.html&quot;&gt;Slides for Scheme 6: Lambda&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tail Call Optimisation in C++ published in Overload journal</title>
          <pubDate>Fri, 22 Jun 2012 08:53:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/06/22/tail-call-optimisation-in-c-published-in-overload-journal/</link>
          <guid>https://artificialworlds.net/blog/2012/06/22/tail-call-optimisation-in-c-published-in-overload-journal/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/06/22/tail-call-optimisation-in-c-published-in-overload-journal/">&lt;p&gt;You &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;read it here first&lt;&#x2F;a&gt;, but now you can have a paper version of &quot;Tail Call Optimisation in C++&quot;, published almost as-is, in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;c310&#x2F;&quot;&gt;Overload 109&lt;&#x2F;a&gt; the journal of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&quot;&gt;ACCU&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme 5: Quotation video</title>
          <pubDate>Thu, 21 Jun 2012 07:26:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/06/21/scheme-5-quotation-video/</link>
          <guid>https://artificialworlds.net/blog/2012/06/21/scheme-5-quotation-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/06/21/scheme-5-quotation-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, Quotation, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video explains on of the most powerful and unfamiliar features of Scheme: quotation. Quoting allows us to talk about and manipulate code in code, as easily as we deal with strings or numbers.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 5: Quotation&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;eb61f84d-1787-4a22-b27c-4a1c806863c4&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-05-quotation&#x2F;scheme-05-quotation.html&quot;&gt;Slides for Scheme 5: Quotation&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme 4: Recursion - how to write functions in Scheme video</title>
          <pubDate>Sat, 16 Jun 2012 18:47:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/06/16/scheme-4-recursion-how-to-write-functions-in-scheme/</link>
          <guid>https://artificialworlds.net/blog/2012/06/16/scheme-4-recursion-how-to-write-functions-in-scheme/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/06/16/scheme-4-recursion-how-to-write-functions-in-scheme/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, Recursion, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video describes the way you write code in Scheme: recursion. Recursive functions can be pretty efficient in Scheme, if you structure them right, and the video covers the concept of a tail call, which is the most important way of doing this.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 4: Recursion&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;585bbc85-9c31-4adb-9c67-36b5ea09409f&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-04-recursion&#x2F;scheme-04-recursion.html&quot;&gt;Slides for Scheme 4: Recursion&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme 3: Closures video</title>
          <pubDate>Fri, 01 Jun 2012 18:38:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/06/01/scheme-3-closures/</link>
          <guid>https://artificialworlds.net/blog/2012/06/01/scheme-3-closures/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/06/01/scheme-3-closures/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, Closures, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video describes an important feature of functions in Scheme: closures. Closures allow functions to carry their &quot;environment&quot; around with them, and even change it later. This allows all kinds of exciting things, including implementing something similar to classes in other languages.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 3: Closures&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e9b49371-1587-478f-ac7b-4c6311ebd996&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-03-closures&#x2F;scheme-03-closures.html&quot;&gt;Slides for Scheme 3: Closures&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme 2: Basics - pairs and lists video</title>
          <pubDate>Fri, 25 May 2012 21:31:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/05/25/scheme-2-basics-pairs-and-lists-video/</link>
          <guid>https://artificialworlds.net/blog/2012/05/25/scheme-2-basics-pairs-and-lists-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/05/25/scheme-2-basics-pairs-and-lists-video/">&lt;p&gt;Series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;Feel the cool&lt;&#x2F;a&gt;, Basics, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Continuing the series on Scheme, this video describes some of the things you will need to understand to be able to write real programs. I introduce pairs, lists, and navigation through lists using recursive procedures, before discussing the map and foldr procedures, which you will use in most programs.&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 2: Basics - pairs and lists&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;e752e05d-e450-492f-a560-ae52d8eb8f84&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;presentations&#x2F;scheme-02-basics&#x2F;scheme-02-basics.html&quot;&gt;Slides for Scheme 2: basics - pairs and lists&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme 1: Feel the cool video</title>
          <pubDate>Tue, 22 May 2012 17:16:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/05/22/scheme-feel-the-cool-video/</link>
          <guid>https://artificialworlds.net/blog/2012/05/22/scheme-feel-the-cool-video/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/05/22/scheme-feel-the-cool-video/">&lt;p&gt;Series: Feel the cool, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;16&#x2F;scheme-4-recursion-how-to-write-functions-in-scheme&#x2F;&quot;&gt;Recursion&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;21&#x2F;scheme-5-quotation-video&#x2F;&quot;&gt;Quotation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;26&#x2F;scheme-6-lambda-video&#x2F;&quot;&gt;Lambda&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;07&#x2F;02&#x2F;scheme-7-macros-video&#x2F;&quot;&gt;Macros&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s me trying to persuade you to be interested in a 40-year-old programming language, called Scheme, which is a type of Lisp:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;Scheme 1: Feel the cool&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;d50ff264-99f8-48b5-826b-a2a7e6c196c1&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-01-intro&#x2F;scheme-01-intro.html&quot;&gt;Slides for Scheme 1: Feel the cool&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Johnny
    &lt;span class=&quot;comment_date&quot;&gt;2023-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hello Andy,&lt;br&#x2F;&gt;
What implementation of Scheme are you using in this fantastic video, is it the one you typically use? I would like to begin learning this implementation.     Thank you so much for your time.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2023-02-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Johnny, I’m using mzscheme: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;racket&#x2F;mzscheme&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;racket&#x2F;mzscheme&lt;&#x2F;a&gt; . Enjoy!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Pete Barber
    &lt;span class=&quot;comment_date&quot;&gt;2012-06-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Cool:–) just watched part 1.  Enjoyable intro.  Look fwd to watching the next couple. Other than fun &amp;amp; cool what was your motivation for learning scheme (writing emacs macros?)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-06-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Pete, thanks!  A friend recommended Structure and Interpretation of Computer Programs, and after a couple of pages I was hooked.  Also I am interested, one day, in writing my own language, so learning Lisp was a necessary prerequisite…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Generalising tail call optimised C++</title>
          <pubDate>Tue, 08 May 2012 22:41:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/05/08/generalising-tail-call-optimised-c/</link>
          <guid>https://artificialworlds.net/blog/2012/05/08/generalising-tail-call-optimised-c/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/05/08/generalising-tail-call-optimised-c/">&lt;p&gt;This series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;27&#x2F;tail-call-optimisation-in-c&#x2F;&quot;&gt;Lightning talk&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;Explanation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;performance-of-tail-call-optimised-c&#x2F;&quot;&gt;Performance&lt;&#x2F;a&gt;, Generalisation.&lt;&#x2F;p&gt;
&lt;p&gt;In previous posts I discussed the construction of some C++ that does the same job that the tail call optimisation does in some other languages. The example code given showed the case where we know that every function in the recursion will take two long integer parameters, and return a long as well.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, it is perfectly possible to generalise this code to cover more complex cases. Fortunately, the trampoline function doesn&#x27;t need to know the arguments taken by the functions being called, only the return value. It looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename RetT&amp;gt;
const RetT trampoline_templ(
    std::auto_ptr&amp;lt; IAnswer&amp;lt;RetT&amp;gt; &amp;gt; answer )
{
    while( !answer-&amp;gt;finished() )
    {
        answer = answer-&amp;gt;tail_call()();
    }
    return answer-&amp;gt;value();
}&lt;&#x2F;pre&gt;
&lt;p&gt;So we can call this with the type of the return value as our template parameter, and supply an object which satisfies the IAnswer&amp;lt;RetT&amp;gt; interface:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename RetT&amp;gt;
class IAnswer
{
public:
    virtual const bool             finished()  const = 0;
    virtual const ICallable&amp;lt;RetT&amp;gt;&amp;amp; tail_call() const = 0;
    virtual const RetT             value()     const = 0;
};&lt;&#x2F;pre&gt;
&lt;p&gt;where ICallable looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename RetT&amp;gt;
class ICallable
{
public:
    typedef std::auto_ptr&amp;lt; IAnswer&amp;lt;RetT&amp;gt; &amp;gt; AnswerPtr;
    virtual AnswerPtr operator()() const = 0;
};&lt;&#x2F;pre&gt;
&lt;p&gt;The concrete classes that implement these interfaces need to know the types (and number) of the arguments, but that&#x27;s ok because they only get instantiated by code that would otherwise (in the standard, non-tail-call recursion case) call the functions themselves. In the toy case we are using of repeatedly adding up numbers to multiply by two, the outer function looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;const long times_two_tail_call_templ( const long n )
{
    typedef Answer2&amp;lt;long, long, long&amp;gt; AnswerType;

    return trampoline_templ(
        AnswerType::newFn(
            times_two_tail_call_impl, 0, n, 0 )
    );
}&lt;&#x2F;pre&gt;
&lt;p&gt;and the inner one looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;std::auto_ptr&amp;lt; IAnswer&amp;lt;long&amp;gt; &amp;gt; times_two_tail_call_impl(
    const long acc, const long i )
{
    typedef Answer2&amp;lt;long, long, long&amp;gt; AnswerType;

    if( i == 0 )
    {
        return return AnswerType::newAns( acc );
    }
    else
    {
        return AnswerType::newFn(
            times_two_tail_call_impl,
            acc + 2, i - 1, 0 );
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Both of the above use static convenience methods newFn and newAns that I have defined on Answer2 to create smart pointers to newly-allocated Answer2 objects. newAns creates an Answer2 that contains a final answer, and newFn creates an Answer2 specifying another function (with arguments) to call.&lt;&#x2F;p&gt;
&lt;p&gt;Answer2 looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename RetT, typename Arg1T, typename Arg2T&amp;gt;
class Answer2 : public IAnswer&amp;lt;RetT&amp;gt;
{
private:
    typedef FnPlusArgs2&amp;lt;RetT, Arg1T, Arg2T&amp;gt; FnArgs;
    typedef std::auto_ptr&amp;lt; IAnswer&amp;lt;RetT&amp;gt; &amp;gt; AnswerPtr;

    const bool finished_;
    const FnArgs tail_call_;
    const RetT value_;

private:
    Answer2( const bool finished, const FnArgs tail_call, const RetT value )
    : finished_( finished )
    , tail_call_( tail_call )
    , value_( value )
    {
    }

    static AnswerPtr newPtr(
        const bool finished, const FnArgs tail_call, const RetT value )
    {
        return AnswerPtr( new Answer2&amp;lt;RetT, Arg1T, Arg2T&amp;gt;(
            finished, tail_call, value ) );
    }
public:
    static AnswerPtr newFn(
        const typename FnArgs::fn_type fn,
        const Arg1T arg1,
        const Arg2T arg2,
        const RetT zero_val )
    {
        return newPtr( false, FnArgs( fn, arg1, arg2 ), zero_val );
    }

    static AnswerPtr newAns( const RetT value )
    {
        return newPtr( true, FnArgs::null(), value );
    }

    virtual const bool    finished()  const { return finished_; };
    virtual const FnArgs&amp;amp; tail_call() const { return tail_call_; };
    virtual const RetT    value()     const { return value_; };
};&lt;&#x2F;pre&gt;
&lt;p&gt;and uses FnPlusArgs2, which looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;template&amp;lt;typename RetT, typename Arg1T, typename Arg2T&amp;gt;
class FnPlusArgs2 : public ICallable&amp;lt;RetT&amp;gt;
{
private:
    typedef typename ICallable&amp;lt;RetT&amp;gt;::AnswerPtr AnswerPtr;
public:
    typedef AnswerPtr (*fn_type)( const Arg1T, const Arg2T );
private:
    const fn_type fn_;
    const Arg1T arg1_;
    const Arg2T arg2_;

public:
    FnPlusArgs2( const fn_type fn, const Arg1T arg1, const Arg2T arg2 )
    : fn_( fn )
    , arg1_( arg1 )
    , arg2_( arg2 )
    {
    }

    virtual AnswerPtr operator()() const
    {
        return fn_( arg1_, arg2_ );
    }

    static FnPlusArgs2&amp;lt;RetT, Arg1T, Arg2T&amp;gt; null()
    {
        return FnPlusArgs2&amp;lt;RetT, Arg1T, Arg2T&amp;gt;( NULL, 0, 0 );
    }
};&lt;&#x2F;pre&gt;
&lt;p&gt;I have continued with the 2 long arguments, and long return value example here, but with the above code it is possible to construct recursive code using more than one function, and the functions can have different numbers of arguments, and different argument types, so long as they all co-operate to produce an answer of the required type. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;tail-call-opt-in-cpp&quot;&gt;Source code for this article&lt;&#x2F;a&gt; includes an example, in the file &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;tail-call-opt-in-cpp&#x2F;src&#x2F;branch&#x2F;master&#x2F;src&#x2F;tail_call_templ_2fns.cpp&quot;&gt;tail_call_templ_2fns.cpp&lt;&#x2F;a&gt;, of two different functions that call each other recursively, and take different arguments, using the trampoline function and interfaces listed above, and Answer3 and FnPlusArgs3 class templates similar to Answer2 and FnPlusArgs2 shown above. Implementing the N-args case using variadic templates (C++11) or template meta-programming is left as an exercise for the reader.&lt;&#x2F;p&gt;
&lt;p&gt;This more realistic case where the number and types of arguments are not known beforehand forces us to use dynamic memory to store our AnswerN objects, and causes more pointer dereferences and virtual function calls, and these do hurt performance. In tests on my machine, this code ran approximately 10 times slower than the version using only stack memory. Perhaps we C++ programmers should comfort ourselves that many languages supporting tail-call optimisation require dynamic memory, virtual functions and pointer indirection to do absolutely everything.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>JavaScript WTFs Videos</title>
          <pubDate>Fri, 04 May 2012 23:16:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/05/04/javascript-wtfs-videos-the-global-object/</link>
          <guid>https://artificialworlds.net/blog/2012/05/04/javascript-wtfs-videos-the-global-object/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/05/04/javascript-wtfs-videos-the-global-object/">&lt;p&gt;I recorded some videos of my JavaScript WTFs presentations:&lt;&#x2F;p&gt;
&lt;iframe title=&quot;JavaScript WTFs 1 - The Global Object&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;f8cbefc8-e162-4bfc-908c-7f8851e65576&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe title=&quot;JavaScript WTFs 2 - Equality and Inequality&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;37cf6064-b22c-400a-b8f5-c26038db296c&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe title=&quot;JavaScript WTFs 3 - Syntax&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;0000076e-90c7-45db-a1b4-751a475d5b20&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe title=&quot;JavaScript WTFs 4 - Reserved words and typeof&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;45efc814-9929-4292-833d-57159f71d6eb&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe title=&quot;JavaScript WTFs 5 - Arithmetic and parseInt&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;3786d456-2211-4fb6-813b-537fcd0119fe&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;iframe title=&quot;JavaScript WTFs 6 - Arrays&quot; width=&quot;560&quot; height=&quot;315&quot; src=&quot;https:&#x2F;&#x2F;video.infosec.exchange&#x2F;videos&#x2F;embed&#x2F;4b50b845-e0d6-4ac8-b85d-4a20007a496d&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot; sandbox=&quot;allow-same-origin allow-scripts allow-popups allow-forms&quot;&gt;&lt;&#x2F;iframe&gt;
&lt;p&gt;You can get the &lt;a href=&quot;&#x2F;presentations&#x2F;javascript-wtfs&#x2F;javascript-wtfs.html&quot;&gt;JavaScript WTFs slides&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Closures in Scheme</title>
          <pubDate>Fri, 04 May 2012 15:11:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/05/04/closures-in-scheme/</link>
          <guid>https://artificialworlds.net/blog/2012/05/04/closures-in-scheme/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/05/04/closures-in-scheme/">&lt;p&gt;&lt;strong&gt;Update: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;06&#x2F;01&#x2F;scheme-3-closures&#x2F;&quot;&gt;watch the video&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this series on Scheme: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;13&#x2F;scheme-feel-the-cool&#x2F;&quot;&gt;Intro&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;24&#x2F;scheme-pairs-lists-and-recursion&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, Closures.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a presentation I did recently, on Closures in the Scheme programming language. Closures are the way the environment in which a function was created hangs around with it as long as the function itself exists. They allow many flexible programming styles, and in this presentation I demonstrate how a simple object-oriented class structure can be implemented using closures.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-03-closures&#x2F;scheme-03-closures.html&quot;&gt;Closures in Scheme&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Performance of tail call optimised C++</title>
          <pubDate>Mon, 30 Apr 2012 21:21:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/30/performance-of-tail-call-optimised-c/</link>
          <guid>https://artificialworlds.net/blog/2012/04/30/performance-of-tail-call-optimised-c/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/30/performance-of-tail-call-optimised-c/">&lt;p&gt;This series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;27&#x2F;tail-call-optimisation-in-c&#x2F;&quot;&gt;Lightning talk&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;Explanation&lt;&#x2F;a&gt;, Performance, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;08&#x2F;generalising-tail-call-optimised-c&#x2F;&quot;&gt;Generalisation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After I wrote a version of &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;tail-call optimised code in C++&lt;&#x2F;a&gt; I became interested in its performance relative to normal recursion. The tail call version can process arbitrarily large input, but how much do you pay for that in terms of performance?&lt;&#x2F;p&gt;
&lt;p&gt;Recap: there are 4 different versions of our function, called times_two. The first, &quot;hardware&quot;, uses the &quot;*&quot; operator to multiply by 2. The second, &quot;loop&quot; uses a for loop to add up lots of 2s until we get the answer. The third, &quot;recursive&quot;, uses a recursive function to add up 2s. The fourth, &quot;tail_call&quot; is a reimplementation of &quot;recursive&quot;, with a manual version of the tail call optimisation - see the original article for details.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s look first at memory usage. Here is the stack memory usage over time as reported by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;valgrind.org&#x2F;docs&#x2F;manual&#x2F;ms-manual.html&quot;&gt;Massif&lt;&#x2F;a&gt; of calling the four functions for a relatively small input value of 100000:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;stack.svg&quot; alt=&quot;Stack usage by the four functions.  recursive uses loads, and the others are constant.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The recursive function uses way more memory than the others (note the logarithmic scale), because it keeps all those stack frames, and the tail_call version takes much longer than the others (possibly because it puts more strain on Massif?), but keeps its memory usage low. Let&#x27;s look at how that affects its performance, for different sizes of input:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;times.svg&quot; alt=&quot;Time taken by each function for different input values.  When recursive starts using more memory than is available on the machine, its execution time grows rapidly&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;For these much larger input values, the recursive and tail_call functions take similar amounts of time, until the recursive version starts using all the physical memory on my computer. At this point, its execution times become huge, and erratic, whereas the tail_call function plods on, working fine.&lt;&#x2F;p&gt;
&lt;p&gt;So the overhead of the infrastructure of the tail call doesn&#x27;t have much impact on execution time for large input values, but it&#x27;s clear from the barely-visible green line at the bottom that using a for-loop with a mutable loop variable instead of function calls is way, way faster, with my compiler, on my computer, in C++. About 18 times faster, in fact.&lt;&#x2F;p&gt;
&lt;p&gt;And, just in case you were wondering: yes those pesky hardware engineers with their new-fangled &quot;*&quot; operator managed to defeat all comers with their unreasonable execution times of 0 seconds every time (to the nearest 10ms). I suppose that shows you something.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tail call optimisation in C++</title>
          <pubDate>Mon, 30 Apr 2012 20:03:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/30/tail-call-optimisation-in-cpp/</link>
          <guid>https://artificialworlds.net/blog/2012/04/30/tail-call-optimisation-in-cpp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/30/tail-call-optimisation-in-cpp/">&lt;p&gt;This series: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;27&#x2F;tail-call-optimisation-in-c&#x2F;&quot;&gt;Lightning talk&lt;&#x2F;a&gt;, Explanation, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;performance-of-tail-call-optimised-c&#x2F;&quot;&gt;Performance&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;08&#x2F;generalising-tail-call-optimised-c&#x2F;&quot;&gt;Generalisation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Some programming languages make recursive programming more practical by providing the tail call optimisation. For a tiny talk at the recent ACCU conference I looked at how we might do something similar in C++.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;codeberg.org&#x2F;andybalaam&#x2F;tail-call-opt-in-cpp&quot;&gt;Source code for this article&lt;&#x2F;a&gt; is available.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;multiplying-by-two&quot;&gt;Multiplying by two&lt;&#x2F;h3&gt;
&lt;p&gt;Here&#x27;s a toy problem we will use as our example.&lt;&#x2F;p&gt;
&lt;p&gt;Imagine for a second that you want to write a function that multiplies a number by two. OK, we can do that:&lt;&#x2F;p&gt;
&lt;pre&gt;long times_two_hardware( long value )
{
    return value * 2;
}&lt;&#x2F;pre&gt;
&lt;p&gt;Now imagine that you don&#x27;t have the &quot;*&quot; operator. We&#x27;re going have to use &quot;+&quot;:&lt;&#x2F;p&gt;
&lt;pre&gt;long times_two_loop( long value )
{
    long ret = 0;
    for ( long i = 0; i &amp;lt; value; ++i )
    {
        ret += 2;
    }
    return ret;
}&lt;&#x2F;pre&gt;
&lt;p&gt;(Obviously, this is just a silly example designed to be easy to follow.)&lt;&#x2F;p&gt;
&lt;p&gt;Now imagine that you read somewhere that state was bad, and you could always replace a loop with recursion. Then you might get something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;long times_two_naive_recursive( long value )
{
    if ( value == 0 )
    {
        return 0;
    }
    else
    {
        return 2 + times_two_naive_recursive( value - 1 );
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;This is fine, but what happens when you run it for a large input?&lt;&#x2F;p&gt;
&lt;pre&gt;$ ulimit -S -s 16
$ .&#x2F;times_two naive_recursive 100000
Segmentation fault&lt;&#x2F;pre&gt;
&lt;p&gt;Note that I set my stack size to be very small (16K) to make the point - actually, this will run successfully for very large arguments, but it will eat all your memory and take a long time to finish.&lt;&#x2F;p&gt;
&lt;p&gt;Why does this fail? Because every time you call a function, the state of the current function is saved, and new information is pushed onto the stack about the new function. When you call a function from within a function multiple times, the stack grows and grows, remembering the state all the way down to the place where you started.&lt;&#x2F;p&gt;
&lt;p&gt;So is programming like this useless in practice?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tail-call-optimisation&quot;&gt;Tail call optimisation&lt;&#x2F;h3&gt;
&lt;p&gt;No, because in several programming languages, the compiler or interpreter performs the &quot;tail call optimisation&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;When you call a function from within some other code, you normally need the state of the current code to be preserved. There is a special case where you don&#x27;t need it, though, and this is called a tail call. A tail call is just the situation where you call a function and immediately return its return value as your return value. In this case, we don&#x27;t need any of the state of the current code any more - we are just about to throw it away and return.&lt;&#x2F;p&gt;
&lt;p&gt;The tail call optimisation throws away this unneeded state before calling the new function, instead of after.&lt;&#x2F;p&gt;
&lt;p&gt;[In practice, in compiled code, this involves popping all the local variables off the stack, pushing the new function parameters on, and jmping to the new function, instead of calling it. This means that when we hit the ret at the end of the new function, we return to the original caller, instead of the location of the tail call.]&lt;&#x2F;p&gt;
&lt;p&gt;Many recursive functions can be re-cast as tail-call versions (sometimes called iterative versions). The one we&#x27;re looking at is one of those. Here is the tail-call version:&lt;&#x2F;p&gt;
&lt;pre&gt;long times_two_recursive_impl( long total, long counter )
{
    if ( counter == 0 )
    {
        return total;
    }
    else
    {
        return times_two_recursive_impl(
            total + 2, counter - 1 );
    }
}

long times_two_recursive( long value )
{
    return times_two_recursive_impl( 0, value );
}&lt;&#x2F;pre&gt;
&lt;p&gt;It consists of an outer function times_two_recursive which just hands off control to the inner function times_two_recursive_impl. The inner function uses a counter variable and calls itself recursively, reducing that counter by one each time, until it reaches zero, when it returns the total, which is increased by 2 each time.&lt;&#x2F;p&gt;
&lt;p&gt;The key feature of this implementation is that the recursive function times_two_recursive_impl uses a tail call to do the recursion: the value of calling itself is immediately returned, without reference to anything else in the function, even temporary variables.&lt;&#x2F;p&gt;
&lt;p&gt;So, let&#x27;s see what happens when we compile and run this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ulimit -S -s 16
$ .&#x2F;times_two recursive 100000
Segmentation fault&lt;&#x2F;pre&gt;
&lt;p&gt;Did I mention that C++ doesn&#x27;t do the tail call optimisation?*&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;* Tail call optimisation isn&#x27;t in the C++ standard. Apparently, some compilers, including MS Visual Studio and GCC, do provide tail call optimisation under certain circumstances (when optimisations are enabled, obviously). It is difficult to implement for all cases, especially in C++ since destruction of objects can cause code to be executed where you might not have expected it, and it doesn&#x27;t appear to be easy to tell when a compiler will or will not do it without examining the generated assembly language. Languages which have this feature by design, like Scheme (and D?) can do it more predictably.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;So how would we write code that is tail call optimised in C++? Possibly of more interest to me personally: if we were generating C++ as the output format for some other language, what code might we generate for tail call optimised functions?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tail-call-optimised-c&quot;&gt;Tail call optimised C++&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s imagine for a second we have some classes, which I&#x27;ll define later. FnPlusArgs holds a function pointer, and some arguments to be passed to it. Answer holds on to one of 2 things: either a FnPlusArgs to call later, or an actual answer (return value) for our function.&lt;&#x2F;p&gt;
&lt;p&gt;Now we can write our function like this:&lt;&#x2F;p&gt;
&lt;pre&gt;Answer times_two_tail_call_impl( long acc, long i )
{
    if ( i == 0 )
    {
        return Answer( true, null_fn_plus_args, acc );
    }
    else
    {
        return Answer(
            false,
            FnPlusArgs(
                times_two_tail_call_impl,
                acc + 2,
                i - 1
            ),
            0
        );
    }
}

long times_two_tail_call( long n )
{
    return trampoline( Answer(
        false,
        FnPlusArgs( times_two_tail_call_impl, 0, n ),
        0 ) );
}&lt;&#x2F;pre&gt;
&lt;p&gt;This has the same structure as times_two_recursive, if a little more verbose. The important point to note, though, is that times_two_tail_call_impl doesn&#x27;t call itself recursively. Instead, it returns an Answer object, which is a delegate saying that we have more work to do: calling the provided function with the supplied arguments.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-trampoline&quot;&gt;The Trampoline&lt;&#x2F;h3&gt;
&lt;p&gt;All we need now is some infrastructure to call this function, and deal with its return value, calling functions repeatedly until we have an answer. This function is called a &quot;trampoline&quot;, and you can sort of see why:&lt;&#x2F;p&gt;
&lt;pre&gt;long trampoline( Answer answer )
{
    while ( !answer.finished_ )
    {
        answer = answer.tail_call_();
    }
    return answer.value_;
}&lt;&#x2F;pre&gt;
&lt;p&gt;While the answer we get back tells us we have more work to do, we call functions, and when we&#x27;re finished we return the answer.&lt;&#x2F;p&gt;
&lt;p&gt;Now all we need to get this working is the definition of Answer and FnPlusArgs:&lt;&#x2F;p&gt;
&lt;pre&gt;struct Answer;
typedef Answer (*impl_fn_type)( long, long );

struct FnPlusArgs
{
    impl_fn_type fn_;
    long arg1_;
    long arg2_;

    FnPlusArgs(
        impl_fn_type fn,
        long arg1,
        long arg2
    );

    Answer operator()();
};

impl_fn_type null_fn = NULL;
FnPlusArgs null_fn_plus_args( null_fn, 0, 0 );

struct Answer
{
    bool finished_;
    FnPlusArgs tail_call_;
    long value_;

    Answer( bool finished, FnPlusArgs tail_call, long value );
};

FnPlusArgs::FnPlusArgs(
    impl_fn_type fn,
    long arg1,
    long arg2
)
: fn_( fn )
, arg1_( arg1 )
, arg2_( arg2 )
{
}

Answer FnPlusArgs::operator()()
{
    return fn_( arg1_, arg2_ );
}

Answer::Answer( bool finished, FnPlusArgs tail_call, long value )
: finished_( finished )
, tail_call_( tail_call )
, value_( value )
{
}&lt;&#x2F;pre&gt;
&lt;p&gt;The only notable thing about this is that we use operator() on FnPlusArgs to call the function it holds.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;results&quot;&gt;Results&lt;&#x2F;h3&gt;
&lt;p&gt;Now, when we run this code, we get what we wanted:&lt;&#x2F;p&gt;
&lt;pre&gt;$ ulimit -S -s 16
$ .&#x2F;times_two tail_call 100000
200000&lt;&#x2F;pre&gt;
&lt;p&gt;(I.e. it doesn&#x27;t crash.)&lt;&#x2F;p&gt;
&lt;p&gt;So, it turns out that the tail call optimisation is just a while loop. Sort of.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    C++ Tail Recursion Using 64-bit variables – Part 2 | Coding Adventures
    &lt;span class=&quot;comment_date&quot;&gt;2015-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] In my previous postÂ I talked aboutÂ recursion problems in a Fibonacci function using 64-bit variables as function parameters , compiling with the Microsoft Visual C++ compiler. It turned out that while thatÂ worked well using 32-bit types it didn’t really usingÂ 64-bit ones. Just as a reminder, Tail RecursionÂ is an optimization performed by the compiler. It is the process ofÂ transforming certain types of tail callsÂ into jumps instead of function calls. More about tail recursion here. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    C++ Tail Recursion Using 64-bit variables – Part 2 – Coding Adventures
    &lt;span class=&quot;comment_date&quot;&gt;2020-01-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] In my previous post I talked about recursion problems in a Fibonacci function using 64-bit variables as function parameters, compiled using the Microsoft Visual C++ compiler. It turned out that while tail recursion was enabled by the compiler using 32-bit types it didn’t really when switching to 64-bit ones. Just as a reminder, Tail Recursion is an optimization performed by the compiler. It is the process of transforming certain types of tail calls into jumps instead of function calls. More about tail recursion here. […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Konstantin
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Function times_two_tail_call( long n ) run tail_call( Answer(…  which is not defined in example&lt;br&#x2F;&gt;
I suppose that function trampoline should run instead or corresponding typedef for tail_call as trampoline should be defined&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Konstantin, thanks – that’s right – it should be trampoline inside times_two_tail_call.  I’ll change it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Lighting talk - Tail call optimisation in C++</title>
          <pubDate>Fri, 27 Apr 2012 20:03:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/27/tail-call-optimisation-in-c/</link>
          <guid>https://artificialworlds.net/blog/2012/04/27/tail-call-optimisation-in-c/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/27/tail-call-optimisation-in-c/">&lt;p&gt;This series: Lightning talk, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;tail-call-optimisation-in-cpp&#x2F;&quot;&gt;Explanation&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;30&#x2F;performance-of-tail-call-optimised-c&#x2F;&quot;&gt;Performance&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;08&#x2F;generalising-tail-call-optimised-c&#x2F;&quot;&gt;Generalisation&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;07&#x2F;30&#x2F;tail-call-optimisation-in-c-lightning-talk-video&#x2F;&quot;&gt;watch the video&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the lightning talk I gave at the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;conferences&#x2F;accu_conference_2012&quot;&gt;ACCU 2012 Conference&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;tail-call-opt-in-cpp&#x2F;tail-call-opt-in-cpp.pdf&quot;&gt;Tail Call Optimisation in C++&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s about how you would generate C++ code that represents a recursive function, without running out of stack space.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scheme: pairs, lists and recursion</title>
          <pubDate>Tue, 24 Apr 2012 09:01:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/24/scheme-pairs-lists-and-recursion/</link>
          <guid>https://artificialworlds.net/blog/2012/04/24/scheme-pairs-lists-and-recursion/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/24/scheme-pairs-lists-and-recursion/">&lt;p&gt;&lt;strong&gt;Update: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;25&#x2F;scheme-2-basics-pairs-and-lists-video&#x2F;&quot;&gt;watch the video&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this series on Scheme: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;13&#x2F;scheme-feel-the-cool&#x2F;&quot;&gt;Intro&lt;&#x2F;a&gt;, Basics, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;04&#x2F;closures-in-scheme&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a presentation I did recently, on the basics of the Scheme programming language.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-02-basics&#x2F;scheme-02-basics.html&quot;&gt;Scheme: pairs, lists and recursion&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What timezone Python thinks I&#x27;m in</title>
          <pubDate>Wed, 18 Apr 2012 22:50:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/18/what-timezone-python-thinks-im-in/</link>
          <guid>https://artificialworlds.net/blog/2012/04/18/what-timezone-python-thinks-im-in/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/18/what-timezone-python-thinks-im-in/">&lt;pre&gt;sudo aptitude install python-dateutil
python -c &quot;import dateutil.tz; import datetime; print datetime.datetime.now( dateutil.tz.tzlocal() ).tzname()&quot;
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Freeview dvb-t scan gives &quot;tuning failed!!!&quot; after 18th April switchover</title>
          <pubDate>Wed, 18 Apr 2012 08:01:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/18/freeview-dvb-t-scan-gives-tuning-failed-after-18th-april-switchover/</link>
          <guid>https://artificialworlds.net/blog/2012/04/18/freeview-dvb-t-scan-gives-tuning-failed-after-18th-april-switchover/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/18/freeview-dvb-t-scan-gives-tuning-failed-after-18th-april-switchover/">&lt;p&gt;Usually when the channel lineup changes, I just have to do this:&lt;pre&gt;$ scan &#x2F;usr&#x2F;share&#x2F;dvb&#x2F;dvb-t&#x2F;uk-CrystalPalace &amp;gt; ~&#x2F;.mplayer&#x2F;channels.conf
&lt;&#x2F;pre&gt;But this time it is failing with:&lt;pre&gt;&amp;gt;&amp;gt;&amp;gt; tune to: 505833000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_3_4:FEC_AUTO:QAM_16:TRANSMISSION_MODE_2K:GUARD_INTERVAL_1_32:HIERARCHY_NONE
WARNING: &amp;gt;&amp;gt;&amp;gt; tuning failed!!!
&amp;gt;&amp;gt;&amp;gt; tune to: 505833000:INVERSION_AUTO:BANDWIDTH_8_MHZ:FEC_3_4:FEC_AUTO:QAM_16:TRANSMISSION_MODE_2K:GUARD_INTERVAL_1_32:HIERARCHY_NONE (tuning failed)
WARNING: &amp;gt;&amp;gt;&amp;gt; tuning failed!!!
&lt;&#x2F;pre&gt;(snip)&lt;&#x2F;p&gt;
&lt;p&gt;However, a more low-level scan with w_scan seems to be working today:&lt;&#x2F;p&gt;
&lt;pre&gt;$ sudo apt-get install w-scan     # Install w_scan
$ w_scan -X &amp;gt; ~&#x2F;.mplayer&#x2F;channels.conf
&lt;&#x2F;pre&gt;
&lt;p&gt;I still can&#x27;t see BBC Four at the moment...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update&lt;&#x2F;strong&gt;It looks like I have to scan when the channels are actually on air. I did this:&lt;pre&gt;$ cd ~&#x2F;.mplayer
$ w_scan -X &amp;gt; channels.conf.morning    # I ran this in the morning
$ w_scan -X &amp;gt; channels.conf.evening    # I ran this in the evening
$ cat channels.conf.morning channels.conf.evening | sort | uniq &amp;gt; channels.conf
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Now I have both BBC Four and CBeebies, which pretty much covers us!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Another way to scan DVB channels: w_scan | The Angry Technician
    &lt;span class=&quot;comment_date&quot;&gt;2013-05-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] messages after that, and no scan results. I’m not sure exactly what is causing this yet, but somebody else in the UK noticed this too, and suspects it began right after a very recent transmitter retune. Luckily, he also had a […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Myatu
    &lt;span class=&quot;comment_date&quot;&gt;2012-06-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Two words: THANK YOU! :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Scheme: feel the cool</title>
          <pubDate>Fri, 13 Apr 2012 16:44:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/04/13/scheme-feel-the-cool/</link>
          <guid>https://artificialworlds.net/blog/2012/04/13/scheme-feel-the-cool/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/04/13/scheme-feel-the-cool/">&lt;p&gt;&lt;strong&gt;Update: &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;22&#x2F;scheme-feel-the-cool-video&#x2F;&quot;&gt;watch the video&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this series on Scheme: Intro, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;04&#x2F;24&#x2F;scheme-pairs-lists-and-recursion&#x2F;&quot;&gt;Basics&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;05&#x2F;04&#x2F;closures-in-scheme&#x2F;&quot;&gt;Closures&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a presentation I did recently, introducing the Scheme programming language (a dialect of Lisp).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;scheme-01-intro&#x2F;scheme-01-intro.html&quot;&gt;Scheme: feel the cool&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Metaprogramming&quot;&gt;Metaprogramming&lt;&#x2F;a&gt; is very powerful, but is usually so hard that we shy away from it. In Scheme, most metaprogramming is just &quot;normal&quot; programming - you can do it in the same language, and in a macro language that is very similar the normal language.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mitch
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, I would like to know if you are using the Pretty Big language in drracket (in your scheme tutorial videos)? Thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-02-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, no I’m using mzscheme, which is apparently the backwards-compatible environment, almost identical to older versions of PLT Scheme.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anushka Yadav
    &lt;span class=&quot;comment_date&quot;&gt;2023-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I watched your video in 2023, 10 years later thought to comment it was really nice …&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2023-05-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    abpccpba
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Just finished your first video about how cool Scheme is. Good job. Now to watch the rest.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-01-02&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks abpccpba, enjoy them :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>JavaScript WTFs</title>
          <pubDate>Fri, 30 Mar 2012 13:41:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/03/30/javascript-wtfs/</link>
          <guid>https://artificialworlds.net/blog/2012/03/30/javascript-wtfs/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/03/30/javascript-wtfs/">&lt;p&gt;I recently did a presentation at work on &quot;surprising&quot; things you may encounter in JavaScript. I present it here for your delectation:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;&#x2F;presentations&#x2F;javascript-wtfs&#x2F;javascript-wtfs.html&quot;&gt;JavaScript WTFs&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sorting arrays was the one that wasted me the most time. Arrays in JavaScript are a neat hack on top of objects. Given that, presumably, all modern implementations have a separate type for an array as opposed to an object, you would hope that at some point they would grow up into something really different in the language.&lt;&#x2F;p&gt;
&lt;p&gt;You can watch the videos here: &lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;05&#x2F;04&#x2F;javascript-wtfs-videos-the-global-object&#x2F;&quot;&gt;JavaScript WTFs videos&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jeffjohnsondicc
    &lt;span class=&quot;comment_date&quot;&gt;2024-11-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;JavaScript quirks never fail to surprise! Sorting arrays being tied to their object-like nature is a classic “gotcha.” Your presentation must have been enlightening for many devs navigating these nuances. It’s a reminder that even mature languages have their peculiarities, keeping us learning and on our toes. Great insight! 🚀&lt;br&#x2F;&gt;
Regards,&lt;br&#x2F;&gt;
&lt;a href=&quot;https:&#x2F;&#x2F;dicc.in&#x2F;&quot; rel=&quot;nofollow&quot;&gt;DICC INSTITUTE&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    andyb
    &lt;span class=&quot;comment_date&quot;&gt;2024-11-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, glad you enjoyed it!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Goodness in programming languages, part 2 &quot; getting your code running</title>
          <pubDate>Thu, 08 Mar 2012 10:03:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/03/08/goodness-in-programming-languages-part-2-getting-your-code-running/</link>
          <guid>https://artificialworlds.net/blog/2012/03/08/goodness-in-programming-languages-part-2-getting-your-code-running/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/03/08/goodness-in-programming-languages-part-2-getting-your-code-running/">&lt;p&gt;&lt;em&gt;Posts in this series: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;02&#x2F;10&#x2F;goodness-in-programming-languages-part-1-syntax-and-layout&#x2F;&quot;&gt;Syntax&lt;&#x2F;a&gt;, Deployment, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;09&#x2F;05&#x2F;goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once&#x2F;&quot;&gt;Metaprogramming&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;01&#x2F;goodness-in-programming-languages-part-4-ownership-memory&#x2F;&quot;&gt;Ownership&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The fancy word for what I&#x27;m talking about here is &lt;strong&gt;Deployment&lt;&#x2F;strong&gt;. How easy is it, once you&#x27;ve written your code, to get it running on someone else&#x27;s computer? What barriers are there to someone else using your program?&lt;&#x2F;p&gt;
&lt;p&gt;Examples of potential barriers include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Having to download some other program first, e.g. a runtime or some kind of dependency.&lt;&#x2F;li&gt;
&lt;li&gt;The actual program being a huge download.&lt;&#x2F;li&gt;
&lt;li&gt;Something mysteriously not working because of different versions of something on the person&#x27;s computer.&lt;&#x2F;li&gt;
&lt;li&gt;The program simply not working on the operating system or machine architecture of the computer.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Anything that can be done to remove these barriers makes my life as the supporter of the program easier, and makes it more likely people will use it.&lt;&#x2F;p&gt;
&lt;p&gt;Here are some things I like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Java&#x27;s ability to run almost anywhere.&lt;&#x2F;strong&gt; Once you have the runtime, Java code is relatively easy to get running on many different operating systems and architectures, using almost-identical code. Other runtime-based languages are also strong here.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Java and Python&#x27;s large built-in libraries.&lt;&#x2F;strong&gt; Both Java and Python include large amounts of functionality in their standard libraries, reducing the need to depend on third-party components.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Python and Perl&#x27;s ability to work out of the box on most Linux systems.&lt;&#x2F;strong&gt; If you are developing for Linux, you can pretty much guarantee that an up-to-date runtime will be available, meaning no dependencies at all are needed.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Many languages&#x27; easy integration with Linux packaging.&lt;&#x2F;strong&gt; Most of the above barriers disappear if you can install dependencies using your operating system&#x27;s built-in package manager.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.ubuntu.com&#x2F;Quickly&quot;&gt;Quickly&lt;&#x2F;a&gt;&#x27;s easy way to build your program into the packaging system.&lt;&#x2F;strong&gt; Things really become easy if your program itself is integrated into the packaging system.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;C and C++&#x27;s lack of dependencies.&lt;&#x2F;strong&gt; It is possible to write C and C++ programs that depend on nothing except standard runtime libraries, which are almost guaranteed to exist on most machines.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;One way to handle dependencies is to package them together with your code. This is what most corporate Java software does - the specific Java runtime that is known to work is packaged with the program itself. This makes for big downloads, and defeats the concept of providing a single package for all platforms, but it does work. It also makes for huge headaches to do with licensing, and is often impossible for software producers who don&#x27;t employ legions of lawyers. It also feels bad and wrong.&lt;&#x2F;p&gt;
&lt;p&gt;When packaging and deploying software, I subscribe to the philosophy of &quot;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.joelonsoftware.com&#x2F;articles&#x2F;fog0000000007.html&quot;&gt;Find the dependencies - and eliminate them&lt;&#x2F;a&gt;&quot;. Until someone can click a single button and have your software running, you haven&#x27;t finished.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam’s Blog » Blog Archive » Goodness in programming languages, part 1 – syntax and layout
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Posts in this series: Syntax, Deployment […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Building jEdit from source</title>
          <pubDate>Sun, 12 Feb 2012 21:17:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/02/12/building-jedit-from-source/</link>
          <guid>https://artificialworlds.net/blog/2012/02/12/building-jedit-from-source/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/02/12/building-jedit-from-source/">&lt;p&gt;I have recently agreed to take over development of the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.jslint.com&#x2F;&quot;&gt;JSLint&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;plugins.jedit.org&#x2F;plugins&#x2F;?JSLint&quot;&gt;plugin&lt;&#x2F;a&gt; for my favourite editor, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;jedit.org&#x2F;&quot;&gt;jEdit&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;To start developing I decided to build jEdit and its plugins from source, which wasn&#x27;t as easy as I would have expected (thanks to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.deffbeff.com&#x2F;blog&#x2F;2009&#x2F;02&#x2F;preparing-for-jedit-plugin-development&#x2F;&quot;&gt;deffbeff&lt;&#x2F;a&gt; for some pointers). A few small changes to the setups for the plugins would make it much easier - at some point I may try to help out there, but for now, here is how I built jEdit and the set of plugins I use regularly:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install openjdk-6-jdk ant git-core subversion

cd ~&#x2F;code&#x2F;public
mkdir jedit
cd jedit

svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;jEdit&#x2F;trunk jEdit

cd jEdit; ant; cd ..

mkdir plugins&lt;&#x2F;pre&gt;
&lt;p&gt;Now I made a file inside plugins called build.properties which looked like this:&lt;&#x2F;p&gt;
&lt;pre&gt;install.dir       = ..&#x2F;..&#x2F;jEdit&#x2F;build&#x2F;settings&#x2F;jars
jedit.plugins.dir = ..&#x2F;..&#x2F;jEdit&#x2F;build&#x2F;settings&#x2F;jars
jedit.install.dir = ..&#x2F;..&#x2F;jEdit&#x2F;build
build.support     = ..&#x2F;build-support&lt;&#x2F;pre&gt;
&lt;p&gt;And then continued like this:&lt;pre&gt;cd plugins
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;build-support&#x2F;trunk build-support&lt;&#x2F;p&gt;
&lt;p&gt;svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;ErrorList&#x2F;trunk ErrorList
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;CommonControls&#x2F;trunk CommonControls
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;InfoViewer&#x2F;trunk InfoViewer
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;Console&#x2F;trunk Console
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;BufferList&#x2F;trunk BufferList
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;FindFile&#x2F;trunk FindFile
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;GnuRegexp&#x2F;trunk GnuRegexp
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;OpenIt&#x2F;trunk OpenIt
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;RecentBufferSwitcher&#x2F;trunk RecentBufferSwitcher
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;Sessions&#x2F;trunk Sessions
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;WhiteSpace&#x2F;trunk WhiteSpace
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;ScriptEnginePlugin&#x2F;trunk ScriptEnginePlugin
svn co https:&#x2F;&#x2F;jedit.svn.sourceforge.net&#x2F;svnroot&#x2F;jedit&#x2F;plugins&#x2F;JavascriptScriptEnginePlugin&#x2F;trunk JavascriptScriptEnginePlugin&lt;&#x2F;p&gt;
&lt;p&gt;git clone git:&#x2F;&#x2F;jedit.git.sourceforge.net&#x2F;gitroot&#x2F;jedit&#x2F;ProjectViewer
git clone git:&#x2F;&#x2F;github.com&#x2F;andybalaam&#x2F;jslint-plugin-for-jedit.git jslint&lt;&#x2F;p&gt;
&lt;p&gt;cd ErrorList; ant; cd ..
cd CommonControls; ant; cd ..
cd InfoViewer; ant; cd ..
cd ProjectViewer; ant; cd ..
cd Console; ant; cd ..
cd BufferList; ant; cd ..
cd FindFile; ant; cd ..
cd GnuRegexp; ant; cd ..
cd OpenIt; ant; cd ..
cd RecentBufferSwitcher; ant; cd ..
cd Sessions; ant; cd ..
cd WhiteSpace; ant build; cd ..
cd ScriptEnginePlugin; ant; cd ..
cd JavascriptScriptEnginePlugin; ant; cd ..
cd jslint; ant; cd ..&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;To find out the dependencies of all plugins and therefore what order to build them, I did this:&lt;pre&gt;find .&#x2F; -name &quot;*.props&quot; | xargs grep &quot;depend.*plugin&quot;&lt;&#x2F;pre&gt;Surely something should do this for me automatically?&lt;&#x2F;p&gt;
&lt;p&gt;The problem I haven&#x27;t solved yet is that the FTP plugin requires JUnit. I could have copied the junit.jar into the jars dir and I think it would have worked, but I want to do it &quot;properly&quot;...&lt;&#x2F;p&gt;
&lt;p&gt;Finally, I made a .desktop shortcut file in ~&#x2F;.local&#x2F;share&#x2F;applications&#x2F;jedit.desktop like this:&lt;&#x2F;p&gt;
&lt;pre&gt;[Desktop Entry]
Name=jEdit
GenericName=Programmer&#x27;s Text Editor
Comment=Edit text files
Exec=java -jar &#x2F;home&#x2F;andy&#x2F;code&#x2F;public&#x2F;jedit&#x2F;jEdit&#x2F;build&#x2F;jedit.jar -settings=&#x2F;home&#x2F;andy&#x2F;code&#x2F;public&#x2F;jedit&#x2F;jEdit&#x2F;build&#x2F;settings %U
Icon=&#x2F;home&#x2F;andy&#x2F;code&#x2F;public&#x2F;jedit&#x2F;jEdit&#x2F;doc&#x2F;jedit.png
Terminal=false
Type=Application
Categories=Development;TextEditor;
StartupNotify=true
MimeType=text&#x2F;plain;
StartupWMClass=org-gjt-sp-jedit-jEdit&lt;&#x2F;pre&gt;
&lt;p&gt;Which enabled me to run the jEdit I had built from my system menu.&lt;&#x2F;p&gt;
&lt;p&gt;Note that I am running jEdit under OpenJDK because Sun&#x2F;Oracle Java is annoying to get hold of at the moment, and generally somewhat evil. Thanks Oracle for killing Java. jEdit &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;jedit&#x2F;+bug&#x2F;347308&quot;&gt;does not work&lt;&#x2F;a&gt; brilliantly with OpenJDK, which is frustrating. Maybe I&#x27;ll try to fix it?&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Warren P
    &lt;span class=&quot;comment_date&quot;&gt;2013-03-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’m curious if anyone who uses jedit from source, ever got jEdit to build from inside either netbeans or eclipse?   I can get jEdit to build from the command line directly with ant but it seems that jedit’s project structure and both Netbeans and Eclipse’s features to import a java project from existing sources or using an existing ant build.xml are seriously limited in ways that prevents them from working at all with the jedit sources.&lt;&#x2F;p&gt;
&lt;p&gt;I asked a question about netbeans+jedit here:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;15693837&#x2F;how-can-i-create-a-netbeans-7-3-project-wrapping-the-jedit-source-code&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;15693837&#x2F;h...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Netbeans even has a wiki page addressing this very subject, but like all things, it’s out of date and no longer works.  (UI changes in Netbeans UI especially around how it works with existing Java projects have rendered Netbeans 7.3 less functional in this area than earlier versions, it seems.)&lt;&#x2F;p&gt;
&lt;p&gt;Warren&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2013-04-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Warren, I haven’t tried this I’m afraid.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Note on a Lucid machine I had to uninstall Eclipse and install the ant 1.8 packages for Natty by manually downloading from here: &lt;a href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;~eclipse-team&#x2F;+archive&#x2F;debian-package&#x2F;+packages&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;launchpad.net&#x2F;~eclipse-team&#x2F;+archive&#x2F;...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Goodness in programming languages, part 1 - syntax and layout</title>
          <pubDate>Fri, 10 Feb 2012 09:42:03 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/02/10/goodness-in-programming-languages-part-1-syntax-and-layout/</link>
          <guid>https://artificialworlds.net/blog/2012/02/10/goodness-in-programming-languages-part-1-syntax-and-layout/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/02/10/goodness-in-programming-languages-part-1-syntax-and-layout/">&lt;p&gt;&lt;em&gt;In this series I will comment on what I like in some of the languages I use. I will cover things that I find convenient, things that might lead me to write correct code, things that tend to make my code more readable, and possibly other things that I just like for no clearly-articulated reason. The purpose of this series is to help me think about what features I would put in a language if I created my own.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Posts in this series: Syntax, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2012&#x2F;03&#x2F;08&#x2F;goodness-in-programming-languages-part-2-getting-your-code-running&#x2F;&quot;&gt;Deployment&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2012&#x2F;09&#x2F;05&#x2F;goodness-in-programming-languages-part-3-not-doing-the-same-thing-more-than-once&#x2F;&quot;&gt;Metaprogramming&lt;&#x2F;a&gt;, &lt;a href=&quot;&#x2F;blog&#x2F;2013&#x2F;05&#x2F;01&#x2F;goodness-in-programming-languages-part-4-ownership-memory&#x2F;&quot;&gt;Ownership&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What aspects of syntax and layout - how the code appears in your text editor - do I like? This is entirely irrelevant to the computer that is running the code, and an implementation detail of the language compiler or interpreter, but is extremely important to me.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Python and Scheme&#x27;s low-symbol approach.&lt;&#x2F;strong&gt; Python and Scheme tend towards the use of English words, rather than symbols to express concepts. This makes code easier to read for someone unfamiliar with the language, and also for me.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Python&#x27;s unambiguous block structure.&lt;&#x2F;strong&gt; Python uses indentation to express block structure (e.g. for variable scope, namespaces and logic structures). Programmers in most languages (notably C-like and Lisp-like ones) tend to use indentation to help humans understand the block structure of their code, but the computer uses different symbols to understand the same thing. This can lead to ambiguity where a human may mis-read the real block structure of some code because the indentation is inconsistent with the symbols. Python avoids the problem by making indentation the syntax understood by the compiler. [Go avoids the same problem by stating that code with inconsistent indenting (as defined by the gofmt program) is invalid.]&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Scheme&#x27;s simplicity.&lt;&#x2F;strong&gt; Scheme (like other Lisp dialects) has very simple rules to describe its syntax. This means you need to learn very little before you can understand the structure of Scheme programs, and it is unlikely you will be confused by rare structures.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, there are trade-offs in all these points. Using fewer symbols can make programs verbose (although I find Python feels very concise). The lack of an end-of-block symbol makes short Python snippets look clean, but can make longer sections hard to understand (perhaps better editor support would help with this?). Scheme&#x27;s use of brackets for everything means there are a lot of brackets, so working out which does what job can be difficult.&lt;&#x2F;p&gt;
&lt;p&gt;Nevertheless, the goals of reducing the number of symbols with special meaning, allowing humans and computers to use the same ways of understanding code, and being as simple as possible are good goals to have.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam’s Blog » Blog Archive » Goodness in programming languages, part 2 â€“ getting your code running
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] in this series: Syntax, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Ethernet not working on Ubuntu with Realtek RTL8111&#x2F;8168B</title>
          <pubDate>Sat, 21 Jan 2012 08:22:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2012/01/21/ethernet-not-working-on-ubuntu-with-realtek-rtl81118168b/</link>
          <guid>https://artificialworlds.net/blog/2012/01/21/ethernet-not-working-on-ubuntu-with-realtek-rtl81118168b/</guid>
          <description xml:base="https://artificialworlds.net/blog/2012/01/21/ethernet-not-working-on-ubuntu-with-realtek-rtl81118168b/">&lt;p&gt;Thanks to rvdavid, the network on my new PC is now working, despite Ubuntu misrecognising it:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.rvdavid.net&#x2F;how-to-get-gigabit-speeds-from-rtl81118168b-pci-express-gigabit-ethernet-controller-on-ubuntu-linux&#x2F;&quot;&gt;How to Get Gigabit Speeds from RTL8111&#x2F;8168B PCI Express Gigabit Ethernet controller on Ubuntu Linux&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is the kind of thing I had to do 10 years ago. I thought we&#x27;d moved on...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide usage stats</title>
          <pubDate>Sun, 16 Oct 2011 07:06:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/10/16/freeguide-usage-stats/</link>
          <guid>https://artificialworlds.net/blog/2011/10/16/freeguide-usage-stats/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/10/16/freeguide-usage-stats/">&lt;p&gt;A long time ago &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2007&#x2F;07&#x2F;31&#x2F;badly-wrong-usage-statistics-for-freeguide&#x2F;&quot;&gt;I mentioned&lt;&#x2F;a&gt; I&#x27;d added some (opt-in) usage tracking for FreeGuide. Here are the latest stats. I think they&#x27;ve held up pretty well given how much I&#x27;ve neglected the project...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;anon_hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;anon_hits-300x120.png&quot; alt=&quot;&quot; title=&quot;anon_hits&quot; width=&quot;300&quot; height=&quot;120&quot; class=&quot;size-medium wp-image-479&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;unique_hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;unique_hits-300x128.png&quot; alt=&quot;&quot; title=&quot;unique_hits&quot; width=&quot;300&quot; height=&quot;128&quot; class=&quot;size-medium wp-image-478&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;hits-300x126.png&quot; alt=&quot;&quot; title=&quot;hits&quot; width=&quot;300&quot; height=&quot;126&quot; class=&quot;size-medium wp-image-477&quot;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Entertain your baby using your Android phone</title>
          <pubDate>Thu, 01 Sep 2011 13:18:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/09/01/entertain-your-baby-using-your-android-phone/</link>
          <guid>https://artificialworlds.net/blog/2011/09/01/entertain-your-baby-using-your-android-phone/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/09/01/entertain-your-baby-using-your-android-phone/">&lt;p&gt;Last night my friend and I uploaded the first version of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;market.android.com&#x2F;details?id=uk.co.monkdesign.babydistraction&quot;&gt;Baby Distraction&lt;&#x2F;a&gt;, an Android app featuring charming cartoon animals, vibration and sounds.&lt;&#x2F;p&gt;
&lt;p&gt;I helped almost exclusively in an advisory capacity, but still am inexplicably proud of the result: it&#x27;s incredibly simple, but polished, and, most importantly, their baby loves it.&lt;&#x2F;p&gt;
&lt;p&gt;This morning I searched for &quot;baby games&quot; on the Android Marketplace, and it was the top hit. Fame and fortune await.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>rdesktop swallows keyboard events</title>
          <pubDate>Wed, 06 Jul 2011 12:03:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/07/06/rdesktop-swallows-keyboard-events/</link>
          <guid>https://artificialworlds.net/blog/2011/07/06/rdesktop-swallows-keyboard-events/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/07/06/rdesktop-swallows-keyboard-events/">&lt;p&gt;The Linux remote desktop client &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.rdesktop.org&#x2F;&quot;&gt;rdesktop&lt;&#x2F;a&gt; grabs all keyboard events by default, which is good, because it allows e.g. you to Alt-Tab between applications and Ctrl-Alt-Del as if you were in front of the remote machine.&lt;&#x2F;p&gt;
&lt;p&gt;However, it is also bad because you can&#x27;t use the standard keyboard shortcuts to switch between virtual desktops. Varius Googling had convinced me that this problem was not soluble, since X only provides a global keyboard-grabbing function &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.xfree86.org&#x2F;current&#x2F;XGrabKeyboard.3.html&quot;&gt;XGrabKeyboard&lt;&#x2F;a&gt;, and once the event has been grabbed and fed to rdesktop, there doesn&#x27;t appear to be a way to re-emit it back up to the window manager so that it can be used to switch desktops.&lt;&#x2F;p&gt;
&lt;p&gt;Kudos to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;sunner.cn&#x2F;&quot;&gt;Sunner&lt;&#x2F;a&gt;, therefore, for coming up with a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.sunner.cn&#x2F;rdesktop-patch-switch-local-workspaces-and-windows-by-local-hot-keys&#x2F;&quot;&gt;workaround&lt;&#x2F;a&gt;. It&#x27;s really simple: you have to press the key twice, and the first time, rdesktop simply ungrabs the keyboard. The second time, the window manager receives the event as normal.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my version of Sunner&#x27;s patch, against rdesktop 1.6.0. It removes Alt-Tab support since I don&#x27;t need that, and adds Ctrl-Alt-Up and -Down, because I do need those:&lt;&#x2F;p&gt;
&lt;pre&gt;--- rdesktop-1.6.0&#x2F;xkeymap.c	2011-07-06 12:49:01.000000000 +0100
+++ rdesktop-1.6.0-mod&#x2F;xkeymap.c	2011-07-06 11:40:24.000000000 +0100
@@ -598,6 +598,25 @@
 				ui_seamless_toggle();
 			break;

+		case XK_Left:
+		case XK_Right:
+		case XK_Up:
+		case XK_Down:
+			if ((get_key_state(state, XK_Alt_L) || get_key_state(state, XK_Alt_R))
+					&amp;amp;&amp;amp; (get_key_state(state, XK_Control_L)
+						|| get_key_state(state, XK_Control_R)))
+			{
+				&#x2F;* Ctrl-Alt-Left&#x2F;Right&#x2F;Up&#x2F;Down:
+				 * Ungrab the keyboard so that user can use Windows manager&#x27;s hot keys *&#x2F;
+				extern RD_BOOL g_fullscreen;
+				if (g_fullscreen) { &#x2F;* Turn to normal window. Otherwise, rdesktop will be always on top *&#x2F;
+					xwin_toggle_fullscreen();
+				}
+
+				XUngrabKeyboard(g_display, CurrentTime);
+				return True;
+			}
+			break;
 	}
 	return False;
 }&lt;&#x2F;pre&gt;
&lt;p&gt;(Download it here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rdesktop-1.6.0-ungrab-on-ctrl-alt-direction.patch&quot;&gt;rdesktop-1.6.0-ungrab-on-ctrl-alt-direction.patch&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;And as usual here is how to patch your Ubuntu system to include this patch:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo apt-get install build-essential fakeroot dpkg-dev
sudo apt-get build-dep rdesktop
mkdir tmp
cd tmp
apt-get source rdesktop
cd rdesktop-*
wget http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;rdesktop-1.6.0-ungrab-on-ctrl-alt-direction.patch
patch -p1 &amp;lt; rdesktop-1.6.0-ungrab-on-ctrl-alt-direction.patch
dpkg-buildpackage -rfakeroot -b
cd ..
sudo dpkg -i rdesktop*.deb&lt;&#x2F;pre&gt;
&lt;p&gt;Now someone just needs to add support for reading your window manager settings to determine automatically what shortcuts you use to switch desktops, and&#x2F;or support for a config file listing keys that make rdesktop ungrab the keyboard.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Scott Smedley
    &lt;span class=&quot;comment_date&quot;&gt;2016-02-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;rdesktop -K&lt;&#x2F;p&gt;
&lt;p&gt;Do  not override window manager key bindings.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-02-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Scott – I want most shortcuts to work in rdesktop, but not all – this is why I had to patch rdesktop, but -K could well be useful for others.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Reclaiming desktop space with Ubuntu&#x27;s window-picker-applet</title>
          <pubDate>Thu, 16 Jun 2011 14:15:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/06/16/reclaiming-desktop-space-with-ubuntus-window-picker-applet/</link>
          <guid>https://artificialworlds.net/blog/2011/06/16/reclaiming-desktop-space-with-ubuntus-window-picker-applet/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/06/16/reclaiming-desktop-space-with-ubuntus-window-picker-applet/">&lt;p&gt;For a long time I&#x27;ve customised GNOME to have a vertical task bar on the left and launcher buttons across the top. I&#x27;ve struggled with a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2008&#x2F;12&#x2F;10&#x2F;fixing-the-vertical-panel-window-list-on-ubuntu-hardy&#x2F;&quot;&gt;long-standing bug with using a vertical panel&lt;&#x2F;a&gt;, and I&#x27;ve sacrificed quite a lot of screen space to make enough room to see the window titles.&lt;&#x2F;p&gt;
&lt;p&gt;Recently I&#x27;ve found myself using smaller screens, and finding certain aspects of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;wiki.ubuntu.com&#x2F;UNR&quot;&gt;Ubuntu Netbook Remix&lt;&#x2F;a&gt; quite good. In particular, I like the combination of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;maximus&quot;&gt;Maximus&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;launchpad.net&#x2F;window-picker-applet&quot;&gt;window-picker-applet&lt;&#x2F;a&gt;, which I combine with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;do.davebsd.com&#x2F;&quot;&gt;GNOME Do&lt;&#x2F;a&gt;. This setup allows me to have almost all of my screen set aside for programs I am using, without feeling lost or inconvenienced.&lt;&#x2F;p&gt;
&lt;p&gt;I need one setting to prevent Maximus from automatically maximising windows - I just want it to remove the title bar when a window is maximised, because window-picker-applet shows the title in my panel instead:&lt;&#x2F;p&gt;
&lt;p&gt;gconftool --type bool --set &#x2F;apps&#x2F;maximus&#x2F;no_maximize true&lt;&#x2F;p&gt;
&lt;p&gt;and then I add Maximus to my Startup Applications (Click Add, give it a name, and set command to &quot;maximus&quot;), and set GNOME Do&#x27;s &quot;Start GNOME Do at login&quot; preference (the Preferences screen can be found on the drop-down at the top right of the GNOME Do window).&lt;&#x2F;p&gt;
&lt;p&gt;Now I am happy, except for a little bug fix and visual tweak I want for window-picker-applet. The relevant bugs are &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;window-picker-applet&#x2F;+bug&#x2F;425813&quot;&gt;Bug #425813&lt;&#x2F;a&gt; (see my patch at the bottom) and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;window-picker-applet&#x2F;+bug&#x2F;798197&quot;&gt;Bug #798197&lt;&#x2F;a&gt;. On an Ubuntu system it&#x27;s amazingly easy to use a patched version:&lt;&#x2F;p&gt;
&lt;p&gt;sudo apt-get install build-essential fakeroot dpkg-dev
sudo apt-get build-dep window-picker-applet
mkdir tmp
cd tmp
apt-get source window-picker-applet
cd window-picker-applet-*
wget http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;window-picker-applet&#x2F;+bug&#x2F;798197&#x2F;+attachment&#x2F;2171550&#x2F;+files&#x2F;show-windows-requring-attention-on-all-workspaces.patch
patch -p1 &amp;lt; show-windows-requring-attention-on-all-workspaces.patch
wget http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;window-picker-applet&#x2F;+bug&#x2F;425813&#x2F;+attachment&#x2F;2171630&#x2F;+files&#x2F;faster-deeper-attention-flash.patch
patch -p1 &amp;lt; faster-deeper-attention-flash.patch
dpkg-buildpackage -rfakeroot -b
cd ..
sudo dpkg -i window-picker-applet_*.deb
killall gnome-panel&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Lanoxx
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi,&lt;&#x2F;p&gt;
&lt;p&gt;If you are interested, there is now a port of window-picker-applet available on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lanoxx&#x2F;window-picker-applet&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;lanoxx&#x2F;window-picker-applet&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Regards&lt;br&#x2F;&gt;
Lanoxx&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Lanoxx, I certainly am interested – thank you very much!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>My Address Book 1.9.0 - rewritten from scratch</title>
          <pubDate>Wed, 11 May 2011 12:58:03 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/05/11/my-address-book-1-9-0-rewritten-from-scratch/</link>
          <guid>https://artificialworlds.net/blog/2011/05/11/my-address-book-1-9-0-rewritten-from-scratch/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/05/11/my-address-book-1-9-0-rewritten-from-scratch/">&lt;p&gt;Ordinarily, my motivations for doing open source work are clear: peer recognition and the satisfaction of knowing people are using my work.&lt;&#x2F;p&gt;
&lt;p&gt;However, I&#x27;ve been distracted from that stuff recently because of my desire to scratch my own itch, by re-writing &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;MyAddressBook&quot;&gt;My Address Book&lt;&#x2F;a&gt; from scratch to allow sharing the same address book between the web interface and the various email programs I use on different computers.&lt;&#x2F;p&gt;
&lt;p&gt;The only way to make it work with those email programs is to store the data in an LDAP server, instead of the custom MySQL database I had used in the original version (which is still available, of course: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;myaddressbook.sf.net&quot;&gt;My Address Book version 1&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;While I was doing it, I took the opportunity (or made the mistake) of re-writing in my favourite language, Python. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.joelonsoftware.com&#x2F;articles&#x2F;fog0000000069.html&quot;&gt;Joel would not approve&lt;&#x2F;a&gt;, but that&#x27;s the fun of open source: I can do whatever I like, no matter how unwise.&lt;&#x2F;p&gt;
&lt;p&gt;I learned a lot of things on the way:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;webpy.org&#x2F;&quot;&gt;web.py&lt;&#x2F;a&gt;. It is a simple, and helpful library in the Python tradition of being concise but powerful.&lt;&#x2F;li&gt;
&lt;li&gt;Setting up Python running as FastCGI on either Apache or lighttpd is &lt;em&gt;much&lt;&#x2F;em&gt; harder than creating a page of PHP. However, once it&#x27;s done, it&#x27;s done. (The results of my research are shown at the bottom of the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;MyAddressBook&#x2F;UserGuideInstall&quot;&gt;Install Guide&lt;&#x2F;a&gt;.)&lt;&#x2F;li&gt;
&lt;li&gt;Setting up an LDAP server to act as a little address book is unbelievably complicated. See the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;MyAddressBook&#x2F;UserGuideInstall&quot;&gt;Install Guide&lt;&#x2F;a&gt; for how to do it. One day, I or someone else will turn all those instructions into the preinstall step of a .deb, and no-one will ever have to worry about it again. Volunteers, please.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;webpy.org&#x2F;docs&#x2F;0.3&#x2F;templetor&quot;&gt;Templetor&lt;&#x2F;a&gt;, web.py&#x27;s templating system is fine, but too slow for large pages. I had to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;myaddressbook.git.sourceforge.net&#x2F;git&#x2F;gitweb.cgi?p=myaddressbook&#x2F;myaddressbook;a=commit;h=ae41d583bd65329bd4fa7fd66498b4b2fa0d6d1a&quot;&gt;reimplement the main address list rendering&lt;&#x2F;a&gt; in plain Python, which made me sad.&lt;&#x2F;li&gt;
&lt;li&gt;I am compulsive about getting my work out into the world.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;It has been a struggle to write all the documentation and think through the installation procedure and all the other stuff that comes with making a public release, but I have been unable to move on to other things until I have got it done. I hope I&#x27;ve done an ok job - the installation procedure is far too complex and should be automated, but if the volume of documentation is any indicator of how helpful it is, it should be reasonable.&lt;&#x2F;p&gt;
&lt;p&gt;I think I found it more difficult than normal because, unlike normal, getting it out to other people was not my main motivation for doing the project. I&#x27;m glad I&#x27;ve worked through it though, and I really hope some people get interested enough to help me make it much easier to use.&lt;&#x2F;p&gt;
&lt;p&gt;I think this project is a good example of how it can be much better to live in the open source world than the proprietory one. If I want to create a small address book for my family in the open source world, I end up with an industrial-strength LDAP server providing it, meaning that so long as someone does the work to make it simple to use for my simple use case, it can scale to solve pretty much any problem I might have in the future. So if I start a small business that eventually grows and needs to track millions of addresses, I can keep using the same LDAP server, on the same hardware, as my original address book.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, My Address Book would have broken long before that. It doesn&#x27;t even do paging, and the home page lists all your addresses in one page.&lt;&#x2F;p&gt;
&lt;p&gt;I must write a rant one day about how I hate paging.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Switching workspace in GNOME via the command line</title>
          <pubDate>Fri, 04 Mar 2011 17:09:42 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/03/04/switching-workspace-in-gnome-via-the-command-line/</link>
          <guid>https://artificialworlds.net/blog/2011/03/04/switching-workspace-in-gnome-via-the-command-line/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/03/04/switching-workspace-in-gnome-via-the-command-line/">&lt;p&gt;I use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.rdesktop.org&#x2F;&quot;&gt;rdesktop&lt;&#x2F;a&gt; to connect to some Windows machines, and it works beautifully. I like to allow it to grab keyboard input so I can switch and close windows on my Windows desktop without fear of accidentally doing something to the rest of my Linux desktop.&lt;&#x2F;p&gt;
&lt;p&gt;However, I have never found a way of letting some keystrokes escape - specifically Ctrl-Alt-Left, Right, Up, Down so that I can switch workspaces (virtual desktops) away from the workspace containing rdesktop.&lt;&#x2F;p&gt;
&lt;p&gt;I have finally found what I hope is a good workaround - I have installed &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;sourceforge.net&#x2F;apps&#x2F;trac&#x2F;easystroke&#x2F;wiki&quot;&gt;easystroke&lt;&#x2F;a&gt; and defined some mouse gestures I can use to switch desktop. These mouse gestures are not swallowed by rdesktop, so they still work, even when it has focus.&lt;&#x2F;p&gt;
&lt;p&gt;When I asked easystroke to send the Ctrl-Alt-Left etc. keystrokes, they got swallowed by rdesktop, so all I needed to be able to do complete the story was a command-line tool to switch workspace.&lt;&#x2F;p&gt;
&lt;p&gt;That turned out to be trickier than you might imagine, but here is my solution, using the amazing &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;tomas.styblo.name&#x2F;wmctrl&#x2F;&quot;&gt;wmctrl&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This should work on GNOME with the metacity window manager, and the standard GNOME workspace switcher. It should be easy to adapt to other window managers and workspace tools.&lt;&#x2F;p&gt;
&lt;p&gt;First, install some tools. On Ubuntu you need:&lt;&#x2F;p&gt;
&lt;p&gt;sudo apt-get install bc wmctrl coreutils grep bash&lt;&#x2F;p&gt;
&lt;p&gt;Now create a file with gedit ~&#x2F;bin&#x2F;workspace-switcher &amp;amp; and edit it to look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

CMD=&quot;$1&quot;

NUM_WORKSPACES=`gconftool-2 --get &#x2F;apps&#x2F;metacity&#x2F;general&#x2F;num_workspaces`
NUM_COLS=`gconftool-2 --get &#x2F;apps&#x2F;panel&#x2F;applets&#x2F;workspace_switcher_screen0&#x2F;prefs&#x2F;num_rows`

NUM_ROWS=`echo &quot;$NUM_WORKSPACES &#x2F; $NUM_COLS&quot; | bc`

CURRENT_WS=`wmctrl -d | grep \* | cut -d &quot; &quot; -f 1`

MOVE_LEFT=&quot;- $NUM_ROWS&quot;
MOVE_RIGHT=&quot;+ $NUM_ROWS&quot;
MOVE_UP=&quot;-1&quot;
MOVE_DOWN=&quot;+1&quot;

case $CMD in

&quot;Left&quot; )
	NEW_WS=`echo $CURRENT_WS &quot;-&quot; $NUM_ROWS | bc`
	if [[ $NEW_WS -lt 0 ]]; then NEW_WS=$CURRENT_WS; fi
	;;

&quot;Right&quot; )
	NEW_WS=`echo $CURRENT_WS &quot;+&quot; $NUM_ROWS | bc`
	if [[ $NEW_WS -ge $NUM_WORKSPACES ]]; then NEW_WS=$CURRENT_WS; fi
	;;

&quot;Up&quot; )
	WS_COL=`echo $CURRENT_WS &quot;%&quot; $NUM_ROWS | bc`
	if [[ $WS_COL -eq 0 ]]; then
	{
		NEW_WS=$CURRENT_WS
	}
	else
	{
		NEW_WS=`echo $CURRENT_WS &quot;- 1&quot; | bc`
	}; fi
	;;

&quot;Down&quot; )
	NEW_WS=`echo $CURRENT_WS &quot;+ 1&quot; | bc`
	NEW_WS_COL=`echo $NEW_WS &quot;%&quot; $NUM_ROWS | bc`
	if [[ $NEW_WS_COL -eq 0 ]]; then NEW_WS=$CURRENT_WS; fi
	;;

* )
	NEW_WS=$CMD

esac

wmctrl -s $NEW_WS&lt;&#x2F;pre&gt;
&lt;p&gt;Make it executable with chmod +x ~&#x2F;bin&#x2F;workspace-switcher and make sure ~&#x2F;bin is in your PATH.&lt;&#x2F;p&gt;
&lt;p&gt;You can run it like this:&lt;&#x2F;p&gt;
&lt;p&gt;switch-workspace Left&lt;&#x2F;p&gt;
&lt;p&gt;to move left - the other possiblities are, obviously, Right, Up and Down.&lt;&#x2F;p&gt;
&lt;p&gt;Or like this:&lt;&#x2F;p&gt;
&lt;p&gt;switch-workspace 3&lt;&#x2F;p&gt;
&lt;p&gt;to move to a workspace by number.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to use it with easystroke, create an action, and for the command simply enter switch-workspace Left and similar as the command.&lt;&#x2F;p&gt;
&lt;p&gt;Easystroke can be installed on Ubuntu like this:&lt;&#x2F;p&gt;
&lt;p&gt;sudo apt-get install easystroke&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jason
    &lt;span class=&quot;comment_date&quot;&gt;2015-09-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Just the piece of magic I was after, thanks.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    thevtm
    &lt;span class=&quot;comment_date&quot;&gt;2016-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I modified the code for just the Up&#x2F;Down&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;thevtm&#x2F;0519bfeaab408c707898&quot; rel=&quot;nofollow&quot;&gt;https:&#x2F;&#x2F;gist.github.com&#x2F;thevtm&#x2F;0519bfeaab408c...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2016-04-05&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Awesome, thanks thevtm!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Amacvar
    &lt;span class=&quot;comment_date&quot;&gt;2014-02-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you, Thank you, Thank you!!&lt;br&#x2F;&gt;
Now I can have many more workspaces than the 12 I was limiting myself to … :) ;)&lt;br&#x2F;&gt;
I’m a visual person …&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Jon
    &lt;span class=&quot;comment_date&quot;&gt;2018-08-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, this gave me the information I needed to fix my workspace key bindings for Pop!_OS&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Bohous Mach
    &lt;span class=&quot;comment_date&quot;&gt;2021-09-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have modified it a bit but thanks a lot for the idea! No more struggle with workspace grid&#x2F;matrix in gnome shell&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2021-09-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Glad it helped!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    how to change workspace by command line - Page 2
    &lt;span class=&quot;comment_date&quot;&gt;2011-03-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] This might be helpful – it is a script to switch workspace using the command line:  &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog...-command-line&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog…-command-line&#x2F;&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dan Connor
    &lt;span class=&quot;comment_date&quot;&gt;2014-05-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;This is awesome.  I am using xmonad on Debian in VirtualBox.  OSX is capturing the super key so I have to use Alt for xmonad.  But this means that the default gnome switching shortcuts are not captured.  But I was able to set up shortcuts to each workspace with your script.  Great!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-06-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Dan, I’m really glad it was helpful.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andrew P.
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;When I check for existence of utilities included by default on my Ubuntu 10.04 system (e.g., &quot;which bash&quot;), I find that bc, grep and bash are already there.  When I try to install coreutils with apt-get, it reports that the latest version is already installed.  The only thing that is missing is wmctrl.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2012-03-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Andrew.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alexander Sorokin
    &lt;span class=&quot;comment_date&quot;&gt;2014-08-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the script! You saved me a bunch of time.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-08-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Alexander, glad it was helpful!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Martin Vahi
    &lt;span class=&quot;comment_date&quot;&gt;2014-10-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;That was useful, unfortunately. :-D&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Anatomy of an interpreter: the Evaluator</title>
          <pubDate>Sat, 12 Feb 2011 01:54:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/02/12/anatomy-of-an-interpreter-the-evaluator/</link>
          <guid>https://artificialworlds.net/blog/2011/02/12/anatomy-of-an-interpreter-the-evaluator/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/02/12/anatomy-of-an-interpreter-the-evaluator/">&lt;p&gt;&lt;em&gt;Posts in this series: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;01&#x2F;anatomy-of-an-interpreter-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;30&#x2F;anatomy-of-an-interpreter-the-parser&#x2F;&quot;&gt;Parser&lt;&#x2F;a&gt;, Evaluator&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m still really enjoying writing my Scheme interpreter &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Subs&quot;&gt;Subs&lt;&#x2F;a&gt;, which can now succesfully run all the example code from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;book.html&quot;&gt;SICP&lt;&#x2F;a&gt; up to section 2.3.4. I&#x27;ve made the changes I mentioned I would in the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;01&#x2F;anatomy-of-an-interpreter-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt; article, so now the Lexer returns Tokens that contain information about their basic types, and I&#x27;ve gone through a significant refactoring to replace one of the several massive switch statements with a virtual function call (&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.c2.com&#x2F;cgi&#x2F;wiki?SwitchStatementsSmell&quot;&gt;Martin Fowler would be proud&lt;&#x2F;a&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;Last time I explained how the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;30&#x2F;anatomy-of-an-interpreter-the-parser&#x2F;&quot;&gt;Parser&lt;&#x2F;a&gt; takes the stream of tokens coming from the Lexer and returns a hierarchical tree of Values, each of which represents an operation or thing in the program.&lt;&#x2F;p&gt;
&lt;p&gt;The Evaluator takes in a tree of Values, &quot;evaluates&quot; it, and returns another Value object, which is the answer. The Evaluator class is by far the most complex part of Subs, so in this post we&#x27;ll start with an overview of how it works. Future posts will break down the different parts in more detail.&lt;&#x2F;p&gt;
&lt;p&gt;The most interesting parts of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;subs-scheme&#x2F;code&#x2F;ci&#x2F;master&#x2F;tree&#x2F;lib&#x2F;evaluator.cpp&quot;&gt;Evaluator&lt;&#x2F;a&gt; class interface look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;class Evaluator
{
public:
    std::auto_ptr&amp;lt;Value&amp;gt; EvalInContext( const Value* value,
        boost::shared_ptr&amp;lt;Environment&amp;gt;&amp;amp; environment );
};&lt;&#x2F;pre&gt;
&lt;p&gt;The EvalInContext method takes in a Value to evaluate, and an &quot;environment&quot; *in which to evaluate it. Note that the in the real code it takes a couple more arguments, including a mysterious and annoying boolean called is_tail_call which will be explained later.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;* More on environments later. All you need to know for now is that they provide a way of keeping hold of all the things we currently know about, identified by name.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A very simplified version of EvalInContext would look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;std::auto_ptr&amp;lt;Value&amp;gt; Evaluator::EvalInContext( const Value* value,
    boost::shared_ptr&amp;lt;Environment&amp;gt;&amp;amp; environment )
{
    if( is_symbol( value ) )
    {
        return eval_symbol( value, environment );
    }

    if( !is_combination( value ) )
    {
        return auto_ptr&amp;lt;Value&amp;gt;( value-&amp;gt;Clone() );
    }

    const CombinationValue* combo = to_combination( value );
    CombinationValue::const_iterator it = combo-&amp;gt;begin();

    auto_ptr&amp;lt;Value&amp;gt; evaldoptr = EvalInContext( *it, environment );

    if( special_symbol( evaldoptr ) )
    {
        return process_special_symbol( evaldoptr, combo );
    }
    else
    {
        ++it;

        CombinationValue argvalues;
        for( ; it != combo-&amp;gt;end(); ++it )
        {
            argvalues.push_back( EvalInContext( *it, environment ).release() );
        }

        return run_procedure( evaldoptr.get(), &amp;amp;argvalues, *this, environment );
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;If the Value to be evaluated is just a symbol, we call eval_symbol which basically looks up the symbol&#x27;s name in the environment and returns the value it finds.&lt;&#x2F;p&gt;
&lt;p&gt;If the Value is not a combination (i.e. the root of a tree of other values) it must be a basic type such as a string or an integer. In this case we simple copy the Value and return it.&lt;&#x2F;p&gt;
&lt;p&gt;Otherwise, it&#x27;s a combination. To evaluate a combination, we follow the &quot;eval-apply&quot; pattern. The principle is to evaluate all the Values in the combination separately, and then &quot;apply&quot; (run) the first value (the &quot;operator&quot;) as a procedure, using the other values as arguments. The first value must evaluate to something that is recognisable as a procedure, or this doesn&#x27;t make sense and we will throw an error.&lt;&#x2F;p&gt;
&lt;p&gt;In practice it&#x27;s a tiny bit more complicated. We evaluate the first Value in the combination (by calling EvalInContext recursively), then we check whether it&#x27;s a special symbol such as if or let and if so, deal with it separately. Otherwise, we evaluate all the other Values (calling EvalInContext recursively again) and put them into a new CombinationValue, and pass the operator and the arguments to run_procedure, which looks something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;std::auto_ptr&amp;lt;Value&amp;gt; run_procedure( const Value* operator,
    const CombinationValue* args, Evaluator&amp;amp; ev,
    boost::shared_ptr&amp;lt;Environment&amp;gt;&amp;amp; environment )
{
    if( is_builtin_procedure( operator ) )
    {
        return handle_builtin_procedure( operator, args, environment );
    }
    else
    {
        std::auto_ptr&amp;lt;Value&amp;gt; ret;

        const CompoundProcedureValue* proc = to_compound_procedure( operator );

        boost::shared_ptr&amp;lt;Environment&amp;gt; new_env =
            proc-&amp;gt;ExtendEnvironmentWithArgs( args );

        for( CombinationValue::const_iterator it = proc-&amp;gt;GetBody()-&amp;gt;begin();
            it != proc-&amp;gt;GetBody()-&amp;gt;end(); ++it )
        {
            ret = ev.EvalInContext( *it, new_env );
        }

        return ret;
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Running a procedure means either doing something built-in (for example adding up two numbers and returning the result) or evaluating some other code, which comes from the definition of the procedure being run. First we call ExtendEnvironmentWithArgs to create a new Environment, which contains the argument Values that were supplied, and then we loop through all the sections of the body of the procedure, evaluating each one. Note that we throw away the returned Values for all sections except the last one (this is how Scheme works).&lt;&#x2F;p&gt;
&lt;p&gt;Once we&#x27;ve evaluated and applied our procedure, which of course potentially includes numerous recursive calls to EvalInContext, we end up with a Value that is returned, and we&#x27;re done.&lt;&#x2F;p&gt;
&lt;p&gt;Simple eh?&lt;&#x2F;p&gt;
&lt;p&gt;But now I must make a confession: almost everything I have written above is lie. Why would I lie to you? Because I missed out something wonderful and strange called &quot;tail-call optimisation&quot;. I&#x27;ll explain next time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to ask technical questions in person</title>
          <pubDate>Sat, 12 Feb 2011 00:27:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2011/02/12/how-to-ask-technical-questions-in-person/</link>
          <guid>https://artificialworlds.net/blog/2011/02/12/how-to-ask-technical-questions-in-person/</guid>
          <description xml:base="https://artificialworlds.net/blog/2011/02/12/how-to-ask-technical-questions-in-person/">&lt;p&gt;In a healthy team performing a technical task, there will be a lot of questions. Those questions will sometimes be asked by those with less technical knowledge, but (in a healthy team) there will be plenty of questions going back the other way too. Questions that come up in the normal course of your work are the main way knowledge is going to be transferred amongst your team members. They are to be encouraged, and you should never feel like a nuisance, or a technical wimp, because you&#x27;re asking them.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s easy to imagine, when you&#x27;re asking someone a question, that whether you get a good answer depends mainly on the person you&#x27;re asking.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, an awful lot depends on you.&lt;&#x2F;p&gt;
&lt;p&gt;To get a good answer to a question, you need to prepare, to manage the Zone, and above all, to make the person you are asking feel clever.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;What follows are some tips on how to ask a technical person a technical question when you&#x27;re in the same room as them. Some of them may also apply to asking on the phone, or even via instant messaging.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-prepare&quot;&gt;1. Prepare&lt;&#x2F;h3&gt;
&lt;p&gt;You&#x27;re about to ask someone a question.&lt;&#x2F;p&gt;
&lt;p&gt;Breathe.&lt;&#x2F;p&gt;
&lt;p&gt;Think about what you are hoping to find out. This might sound obvious, but when you&#x27;re in the thick of trying to solve a problem, you may find you&#x27;re actually quite confused about what you&#x27;re doing. If you can, you want to avoid wasting the other person&#x27;s time with your confusion. Even worse, you might confuse them, and then they won&#x27;t feel clever.&lt;&#x2F;p&gt;
&lt;p&gt;This doesn&#x27;t need to take long - it may take half a second, but just let it cross your mind - what do I want to find out?&lt;&#x2F;p&gt;
&lt;p&gt;Think about the other person: what are they doing right now? Is it related? What do they know about? From what angle will they be approaching your question? Again, this will probably take almost no time at all, but it will help you ask your question well.&lt;&#x2F;p&gt;
&lt;p&gt;Now you need to zoom out. You are deep into a problem, so deep in fact that you&#x27;re stuck on a very specific part of it. This part of it is burning into your consciousness, obscuring the wider picture of what you&#x27;re doing. The person you&#x27;re asking is going to need that wider picture, so remind yourself what it is.&lt;&#x2F;p&gt;
&lt;p&gt;Once you&#x27;ve zoomed out, you will need to engage with the Zone.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-manage-the-zone&quot;&gt;2. Manage the Zone&lt;&#x2F;h3&gt;
&lt;p&gt;The biggest problem you have when asking a technical person a technical question is that they may well be in the Zone. The Zone is a state of intense concentration in which you get really good work done (Rands elaborates in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.randsinrepose.com&#x2F;archives&#x2F;2006&#x2F;07&#x2F;10&#x2F;a_nerd_in_a_cave.html&quot;&gt;A Nerd in a Cave&lt;&#x2F;a&gt;). The thing about the Zone is that it is difficult (sometimes even painful) to come out of.&lt;&#x2F;p&gt;
&lt;p&gt;If the person you&#x27;re asking is in the Zone, they will need to be coaxed out. When you start talking, even if they turn and look at you and say something encouraging, they are somewhere far away, solving a problem. Your problem is an unwelcome intrusion.&lt;&#x2F;p&gt;
&lt;p&gt;The first lesson about the Zone is: don&#x27;t say much at first.&lt;&#x2F;p&gt;
&lt;p&gt;When you start talking the other person doesn&#x27;t know whether you&#x27;re going to offer them a cup of tea, talk politics, or ask them how to implement quicksort. They haven&#x27;t disengaged at all from the problem they are working on. You need to let them know you are going to ask them a technical question, and start the painful process of pulling them out of their problem, and into yours.&lt;&#x2F;p&gt;
&lt;p&gt;Often the best thing to do is pick a single word or phrase that lets them know the general area you are talking about: for example, &quot;import&quot;, or &quot;the C++ standard&quot;, or &quot;garbage collection&quot;. If you say something like that, and watch their eyes, you can almost see the glaze of the Zone lifting.&lt;&#x2F;p&gt;
&lt;p&gt;If you can see they&#x27;re still elsewhere, give them a second - maybe they&#x27;ll even say something like, &quot;I just want to finish this line.&quot; If they do, let them - step back, take the pressure off - they will be much better able to answer you when the thing they need to remember is written down.&lt;&#x2F;p&gt;
&lt;p&gt;Now your job is to zoom them in on your problem, at their pace. It&#x27;s very important to watch and listen to the other person while you do this. Give them a problem area, and check they understood what you meant. Now give them closer context - maybe the directory you&#x27;re talking about, or the DLL you&#x27;re working on. Now tell them what class you&#x27;re editing, or what file.&lt;&#x2F;p&gt;
&lt;p&gt;As soon as you can see it&#x27;s needed, be quick to show them the exact thing you&#x27;re doing on your screen or paper - that way they can add any extra context they need themselves. Be very wary of drawing vague diagrams on paper or a whiteboard - they confuse more often than they enlighten - much better to show them the real code or document you are working on. They can handle it.&lt;&#x2F;p&gt;
&lt;p&gt;All the time, watch how they are reacting - are they remembering what you&#x27;re talking about, or completely in the dark? You may need to choose a different way into the problem. Remember, at this stage it&#x27;s your job to explain what you mean, not their job to guess.&lt;&#x2F;p&gt;
&lt;p&gt;If you do this well, they&#x27;re slightly in the Zone again, but this time focussed on your problem area. Now, and only now, hit them with your question. They&#x27;ll know exactly what you&#x27;re talking about, and feel really clever, especially if they know the answer.&lt;&#x2F;p&gt;
&lt;p&gt;There are a couple other things you need to know about the Zone. As we know, it can be painful to be pulled out of the Zone. That means that the person you&#x27;re talking to, no matter how nice they are, and how much they like you, may feel a distinct feeling of irritation when you start talking. To make matters worse, they were concentrating, so probably frowning already. Even if they don&#x27;t feel any irritation at all, they may look like they do. What can you do about this? Not a lot, except don&#x27;t take it personally - it&#x27;s normally momentary, and experienced technical people will recognise it&#x27;s a false feeling, almost a physical reaction, and put it to one side immediately.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, understanding the Zone explains why preparation is so important. It&#x27;s very likely you are in the Zone yourself at the beginning of this process. You are submerged in a difficult problem, and quite possibly deeply irritated by your inability to solve it. To ask a question effectively (and thus get a good answer) you need to pull yourself out of the Zone and back into human interaction mode. This includes ensuring your annoyance is fully dissipated, or you&#x27;re going to say something you shouldn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;Throughout all this you need to make it your mission to make the other person feel clever.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-make-them-feel-clever&quot;&gt;3. Make them feel clever&lt;&#x2F;h3&gt;
&lt;p&gt;Why would you want to make the person you&#x27;re asking feel clever?&lt;&#x2F;p&gt;
&lt;p&gt;I certainly don&#x27;t mean flatter them. If they&#x27;re deep in their work they don&#x27;t want you to waste their time telling them how good they are at something.&lt;&#x2F;p&gt;
&lt;p&gt;Think about it this way: how likely is it that you&#x27;ll get an answer if you&#x27;ve made them feel stupid? I don&#x27;t mean you won&#x27;t get an answer because they&#x27;re too annoyed with you to bother - I mean they&#x27;re feeling stupid because you&#x27;ve confused them, and so they have no idea what the answer is.&lt;&#x2F;p&gt;
&lt;p&gt;Bear in mind that it&#x27;s really easy to confuse someone. In your technical problem area there are thousands, or possibly even millions, of tiny micro-contexts that make sense within themselves, but sound like gibberish if you&#x27;re outside that context.&lt;&#x2F;p&gt;
&lt;p&gt;Turning to someone and saying something like, &quot;How do I Frobnish the Pernicator?&quot; without first reminding them that Pernicators get created every time the TunableBeadnest is RePreppered is very hard work for them: they need to ask you a series of questions before they can work out what you&#x27;re asking. Making them do this work makes them feel like they aren&#x27;t clever enough to hold the entire knowledge of your arena in the front of their mind simultaneously. Of course, no-one is able to do this, but everyone feels like they ought to be. Don&#x27;t make them feel stupid.&lt;&#x2F;p&gt;
&lt;p&gt;Even worse is to say something like, &quot;How do I call this method?&quot; In this case, even if they had total front-of-brain memory of the entire arena, they still couldn&#x27;t answer this question, without interrogating you about what you meant.&lt;&#x2F;p&gt;
&lt;p&gt;You goal should be that the first thing they have to say in the whole interaction is when they give you the answer, or at least ask a question that shows they have grasped what you&#x27;re asking, and makes them feel clever.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, making them feel clever also makes them more inclined to talk to you next time you have a question, but that&#x27;s merely a side benefit. The real reason you want to make them feel clever is that when they feel like that, it means you have given them the information they need, in the order they want, with timing that works for them, so that they can give you a great answer.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Molly
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Andy,&lt;&#x2F;p&gt;
&lt;p&gt;You clearly explained what I have dimly sensed (from NOT asking questions right, or at the right moment) for years.  Thank you for explaining this to me and everyone else.  Now, on to asking things the right way!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2014-04-25&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Molly, really glad you found it helpful!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Anatomy of an interpreter: the Parser</title>
          <pubDate>Thu, 30 Sep 2010 23:38:17 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2010/09/30/anatomy-of-an-interpreter-the-parser/</link>
          <guid>https://artificialworlds.net/blog/2010/09/30/anatomy-of-an-interpreter-the-parser/</guid>
          <description xml:base="https://artificialworlds.net/blog/2010/09/30/anatomy-of-an-interpreter-the-parser/">&lt;p&gt;&lt;em&gt;Posts in this series: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;01&#x2F;anatomy-of-an-interpreter-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt;, Parser, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2011&#x2F;02&#x2F;12&#x2F;anatomy-of-an-interpreter-the-evaluator&#x2F;&quot;&gt;Evaluator&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Subs&quot;&gt;Subs&lt;&#x2F;a&gt; has reached version 1.3.4, which means that it can successfully run all the tests from chapter 1 of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;book.html&quot;&gt;SICP&lt;&#x2F;a&gt;. This is very exciting.&lt;&#x2F;p&gt;
&lt;p&gt;Last time I explained a bit about the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;01&#x2F;anatomy-of-an-interpreter-the-lexer&#x2F;&quot;&gt;Lexer&lt;&#x2F;a&gt;, which takes in a stream of characters and emits a stream of tokens: individual elements of code such as a bracket, a keyword or a symbol.&lt;&#x2F;p&gt;
&lt;p&gt;Generally, parsers emit some kind of tree structure - they understand the raw tokens as a hierarchical structure which (conceptually, at least) will be executed from the bottom up, with each branch-point in the tree being an operation of some kind.&lt;&#x2F;p&gt;
&lt;p&gt;Our parser takes in a stream of tokens, and emits a stream of parsed trees.&lt;&#x2F;p&gt;
&lt;p&gt;Parsing Scheme is very easy, because (except for a couple of exceptions I haven&#x27;t implemented yet) there is essentially one rule: start with an open bracket, see a list of things, and then find a close bracket. Of course, one of the &quot;things&quot; you see may itself be another bracketted list, so after parsing you get a tree structure of nested lists.&lt;&#x2F;p&gt;
&lt;p&gt;The parser in Subs looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;class Parser
{
public:
    Parser( ILexer&amp;amp; lexer );
    std::auto_ptr&amp;lt;Value&amp;gt; NextValue();
private:
    ILexer&amp;amp; lexer_;
};&lt;&#x2F;pre&gt;
&lt;p&gt;We supply a Lexer in the constructor, which we know will provide us with tokens when we need them via its NextToken() method. The Parser&#x27;s NextValue() method returns a pointer to a Value, which is the base class for all the &quot;things&quot; in the Subs interpreter.&lt;&#x2F;p&gt;
&lt;p&gt;There are lots of types of things that inherit from the Value class, but the &quot;parse tree&quot; (the output of the parser) will only consist of a very small subset of them:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CombinationValue&lt;&#x2F;li&gt;
&lt;li&gt;DecimalValue&lt;&#x2F;li&gt;
&lt;li&gt;IntegerValue&lt;&#x2F;li&gt;
&lt;li&gt;StringValue&lt;&#x2F;li&gt;
&lt;li&gt;SymbolValue&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The CombinationValue class forms the tree structure. Its declaration looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;class CombinationValue : public Value, public std::vector&amp;lt;Value*&amp;gt;
{
    &#x2F;&#x2F; ...
};&lt;&#x2F;pre&gt;
&lt;p&gt;It is simply a list of other Values.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that it &quot;owns&quot; those Values in the sense that it deletes them when it is deleted. I have recently made the jump to make Subs depend on BOOST, so it&#x27;s on my TODO list to make containers like this use the BOOST smart containers to manage this job for me.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;DecimalValue, IntegerValue and StringValue are relatively self-explanatory: they contain numbers and strings that were found as literals in the source code.&lt;&#x2F;p&gt;
&lt;p&gt;SymbolValue is essentially everything else - if the code that recognises the type of a token can&#x27;t recognise it as a bracket, a number or a string, we assume it is a symbol, and tuck it away in a SymbolValue to be understood later.&lt;&#x2F;p&gt;
&lt;p&gt;The core of the Parser looks like this (with some error-checking removed):&lt;&#x2F;p&gt;
&lt;pre&gt;std::auto_ptr&amp;lt;Value&amp;gt; next_value( ILexer&amp;amp; lexer, Token token )
{
    if( token.Name() == &quot;(&quot; )
    {
        auto_ptr&amp;lt;CombinationValue&amp;gt; ret( new CombinationValue );
        while( true )
        {
            token = lexer.NextToken();
            if( token.Name() == &quot;)&quot; )
            {
                break;
            }
            &#x2F;&#x2F; Recursive call
            ret-&amp;gt;push_back( next_value( lexer, token ).release() );
        }
        return auto_ptr&amp;lt;Value&amp;gt;( ret.release() );
    }
    else
    {
        return ValueFactory::CreateValue( token );
    }
}&lt;&#x2F;pre&gt;
&lt;p&gt;(Full code here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;subs-scheme&#x2F;code&#x2F;ci&#x2F;master&#x2F;tree&#x2F;lib&#x2F;parser.cpp&quot;&gt;Parser.cpp&lt;&#x2F;a&gt;) It&#x27;s a simple recursive function that creates a CombinationValue whenever it finds a bracket, and otherwise uses a ValueFactory to create an individual value.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Side note: the wisdom of using recursion could certainly be questioned, since it limits the depth of bracketting we can handle to the size of the C++ stack, but the only other way to get the same result would be to keep our own manual stack of unfinished combinations, and it just seems perverse to re-implement language features like that. What might well be more interesting would be to consider whether we can actually evaluate parts of the tree as we go, without parsing it all at once. This might make the whole setup scale rather better, but would most likely be quite complex. The implementation presented here will work fine for almost any imaginable program - remember we would need not just code whose &lt;em&gt;execution&lt;&#x2F;em&gt; is deeply nested, but whose &lt;em&gt;expression&lt;&#x2F;em&gt; in code had thousands of levels of nesting before the parser would fail.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;subs-scheme&#x2F;code&#x2F;ci&#x2F;master&#x2F;tree&#x2F;lib&#x2F;valuefactory.cpp&quot;&gt;ValueFactory&lt;&#x2F;a&gt; uses some basic rules such as &quot;starts and ends with a quote&quot; or &quot;consists of only numbers and a decimal point&quot; to recognise what type of Value to create, and if no rules match it defaults to a SymbolValue.&lt;&#x2F;p&gt;
&lt;p&gt;When we have completed a bracketted expression, we return a complete tree of numbers, strings and symbols, and it is ready to be evaluated, which you can think of as simply expanding the tree we already have into the full expression of itself, and then reducing it back down again to an answer.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, the Evaluator and the famous eval-apply loop.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Anatomy of an interpreter: the Lexer</title>
          <pubDate>Wed, 01 Sep 2010 23:27:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2010/09/01/anatomy-of-an-interpreter-the-lexer/</link>
          <guid>https://artificialworlds.net/blog/2010/09/01/anatomy-of-an-interpreter-the-lexer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2010/09/01/anatomy-of-an-interpreter-the-lexer/">&lt;p&gt;&lt;em&gt;Posts in this series: Lexer, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2010&#x2F;09&#x2F;30&#x2F;anatomy-of-an-interpreter-the-parser&#x2F;&quot;&gt;Parser&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2011&#x2F;02&#x2F;12&#x2F;anatomy-of-an-interpreter-the-evaluator&#x2F;&quot;&gt;Evaluator&lt;&#x2F;a&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I have been having a lot of fun recently writing my Scheme interpreter &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Subs&quot;&gt;Subs&lt;&#x2F;a&gt;. I have never implemented a full programming language before, so I am learning fast (mostly through mistakes) and wanted to write down some of the stuff I am discovering.&lt;&#x2F;p&gt;
&lt;p&gt;Note: if you want to learn more about what Scheme is I recommend &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Scheme_%28programming_language%29&quot;&gt;Scheme (Wikipedia)&lt;&#x2F;a&gt; and the book &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;book.html&quot;&gt;SICP&lt;&#x2F;a&gt;, which is the inspiration for all this.&lt;&#x2F;p&gt;
&lt;p&gt;I am writing everything from scratch, just because it&#x27;s fun (certainly not because I think it is in any way better to do it that way...). As we will see, that gives me opportunities to do things in different ways from the normal way such things are done. So far, every time I find out I&#x27;ve deviated from the normal way I&#x27;ve quickly discovered why I am wrong, and had to learn the true path.&lt;&#x2F;p&gt;
&lt;p&gt;Text-based programming languages, whether interpreted or compiled, need a lexer. A lexer takes in characters and spits out &quot;tokens&quot;, which are groups of characters that represent a single thing, such as a bracket, a variable name or a number. (Those tokens are then passed on to the parser, which I will cover in a different post.)&lt;&#x2F;p&gt;
&lt;p&gt;Scheme (and other Lisp variants) are fairly easy to lex because they don&#x27;t have much syntax - you just need to be able to understand round brackets, numbers and strings, and a couple of special cases that I won&#x27;t go into because I haven&#x27;t actually implemented them yet. (Mind you, I haven&#x27;t implemented strings yet either...)&lt;&#x2F;p&gt;
&lt;p&gt;When I started Subs I took my normal approach of doing whatever I wanted without any research or even much thought, and wrote something that I called a lexer, but which was really something else. It took in a stream of characters, read it one &quot;word&quot; at a time (using whitespace as separators), broke up the word if it contained bracket characters, and emitted a tree structure with each branch representing a bracketted list. It just seemed sensible, while I was watching the brackets flow by, to understand them and create a tree structure.&lt;&#x2F;p&gt;
&lt;p&gt;However, for a number of reasons, that approach turned out to be wrong.&lt;&#x2F;p&gt;
&lt;p&gt;First, reading a &quot;word&quot; at a time made things much harder than simply stepping through each character. It made my initial implementation slightly faster, but as soon as I realised I cared about white space (e.g. keeping track of what line we are on) it had to go. When it went it also turned out to be easier to deal with unusual code layout - for example &quot;a(b&quot; should be lexed as 3 tokens, but would be handed to us as a single word.&lt;&#x2F;p&gt;
&lt;p&gt;Second, and more importantly, creating a tree structure at this point was a waste of time. Creating tree structures is normally the job of a parser, and mixing these responsibilities gave me some pointless inefficiency: the lexer emitted a tree of tokens, which the parser then translated into another tree (of fully-understood code objects). It turned out that walking the tree of tokens and copying that structure in the parser was at least as hard as just taking in a flat stream of tokens and constructing the tree just once.&lt;&#x2F;p&gt;
&lt;p&gt;So, I re-wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;subs-scheme&#x2F;code&#x2F;ci&#x2F;master&#x2F;tree&#x2F;lib&#x2F;lexer.h&quot;&gt;Lexer&lt;&#x2F;a&gt; into something that is starting to become worthy of the name. The most interesting parts of its signature look like this:&lt;&#x2F;p&gt;
&lt;pre&gt;class Lexer
{
public:
    Lexer( std::istream&amp;amp; instream );
    Token NextToken();
};
&lt;&#x2F;pre&gt;
&lt;p&gt;It takes in a reference to a stream, which will provide the characters, and when NextToken is called, it reads enough characters to determine what the next token will be, and returns it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Side note: Subs is written using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.martinfowler.com&#x2F;bliki&#x2F;TestDrivenDevelopment.html&quot;&gt;Test-Driven Development&lt;&#x2F;a&gt;. I re-implemented the Lexer and Parser from scratch (naming the new classes NewLexer and NewParser until they were ready), modified the code that used them to use the new interfaces, ran the tests, and immediately knew that the new classes worked as well as the old ones. That level of confidence is incredibly freeing. I can&#x27;t imagine how I would ever have convinced myself the new classes were ready had I not had that safety net of 100s of tests that ensure the interpreter correctly responds to each type of input.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Currently the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;p&#x2F;subs-scheme&#x2F;code&#x2F;ci&#x2F;master&#x2F;tree&#x2F;lib&#x2F;token.h&quot;&gt;Token&lt;&#x2F;a&gt; class it returns is pretty much just a string, with some information attached about where that string was in the original text. In researching this article I realised that most lexers attach more information than that to their tokens - they understand its basic type such as integer, decimal, string, bracket or symbol. At the moment in Subs, this work is done in the parser (so for the lexer each token is just a string), but I can see why it is helpful to do this work in the lexer, because for most types we have the information anyway. For example, in order to recognise that &#x27;&quot;foo (bar&quot;&#x27; (where the double-quotes are in the real code) is a single token we must understand that it is a string. Since we know it at this stage, we might as well record it in the Token object so we don&#x27;t have to work it out again later. When Subs supports strings, I will probably add a &quot;type&quot; field to the token and move this work from the parser.&lt;&#x2F;p&gt;
&lt;p&gt;On a more general programming point, following on from comments I made in a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;2009&#x2F;11&#x2F;20&#x2F;dont-design-for-performance-until-its-too-late&#x2F;&quot;&gt;previous post&lt;&#x2F;a&gt;, it is worth noting that the structure of the lexer (and, as we will see later, the parser) uses a technique called &quot;streams&quot;. What this means is that we write functions like NextToken that process a small part of the total problem, and return their answer. If we chain together functions like that (for example the parser&#x27;s equivalent function calls NextToken whenever it needs a new token) we can process arbitrarily large input without using lots of memory or slowing down. The lexer is able to process any number of tokens using a very small amount of memory, and will only fall over if it encounters a single token that is ridiculously large.&lt;&#x2F;p&gt;
&lt;p&gt;The stream style can be very useful for some problems, not only because it can be efficient with memory, but also because it can help break problems into neat, small pieces that are easier to implement correctly. It is also useful for writing code in a functional style, because it allows us to avoid having any internal state (e.g. we could easily implement NextToken to take in the stream it should read from and avoid any member variables at all in Lexer), by pushing state out into the input and output, instead of having it in our program. In this case that means instead of reading, storing and then processing a program, our lexer can simply process a few characters and emit a token without knowing anything about the surrounding code or wider context. This makes it much easier to test, and (potentially) easier to do clever things with, like prove mathematically that it is correct (!)&lt;&#x2F;p&gt;
&lt;p&gt;Next time, the parser.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Muhammad
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Really helpful. Recently, I have been trying to write a markdown parser. And I got stuck on some points which was clearly explained in the lexer and parser articles.&lt;br&#x2F;&gt;
Would you please update the link to the code?&lt;br&#x2F;&gt;
Thanks&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Muhammad, I’m glad it was helpful.  Thanks for pointing out the broken links – I have fixed them in all three articles.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Muhammad
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank you&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Subs Scheme Lisp Interpreter</title>
          <pubDate>Wed, 18 Aug 2010 16:05:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2010/08/18/subs-scheme-lisp-interpreter/</link>
          <guid>https://artificialworlds.net/blog/2010/08/18/subs-scheme-lisp-interpreter/</guid>
          <description xml:base="https://artificialworlds.net/blog/2010/08/18/subs-scheme-lisp-interpreter/">&lt;p&gt;Why would you write a Lisp interpreter?&lt;&#x2F;p&gt;
&lt;p&gt;I find that question difficult to answer, but the joy of open source is that I don&#x27;t have to answer it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Subs&quot;&gt;Subs&lt;&#x2F;a&gt; is a Scheme interpreter written in C++, and growing out of the excitement I have felt while reading &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;book.html&quot;&gt;Structure and Interpretation of Computer Programs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Subs is very incomplete, and will probably never be otherwise, but it is exciting how quickly you can write a functional Lisp interpreter. I plan to go through SICP section by section and ensure all the examples work in Subs. So far I am failing on the last example in section 1.1.1 (because I don&#x27;t support new-lines inside statements yet!) but in reality most of the work is done to support quite a lot of stuff (NOT including mutable variables...yuck).&lt;&#x2F;p&gt;
&lt;p&gt;One possible explanation, if such a thing were necessary, is that one day I want to write a new programming language that has all the simplicity and metaprogramming capability of Lisp, with the native performance and deployability of C++, and the syntactic elegance of Python. I&#x27;ve got a lot to learn first.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Scalable Graph Coverage</title>
          <pubDate>Mon, 21 Jun 2010 00:25:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2010/06/21/scalable-graph-coverage/</link>
          <guid>https://artificialworlds.net/blog/2010/06/21/scalable-graph-coverage/</guid>
          <description xml:base="https://artificialworlds.net/blog/2010/06/21/scalable-graph-coverage/">&lt;p&gt;If you&#x27;re interested in dealing with large directed graphs of dependent objects and want some tips on how to process them in a way that scales in terms of memory usage, you may be interested in the article I wrote for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;overloadonline&quot;&gt;Overload&lt;&#x2F;a&gt;, which is a journal of the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&quot;&gt;ACCU&lt;&#x2F;a&gt; (an organisation promoting &quot;professionalism in programming&quot;).&lt;&#x2F;p&gt;
&lt;p&gt;The article is &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;index.php&#x2F;journals&#x2F;1632&quot;&gt;Scalable Graph Coverage&lt;&#x2F;a&gt;, or, in my original title, &quot;Comparing scalable algorithms for walking all nodes of a dependency graph&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;It contains lots of code, written in C++, using BOOST Graph Library. The code demonstrates some of the algorithms that are available for choosing batches of nodes to be processed together to reduce the number of nodes that are loaded several times, and without running out of memory.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Ubuntu Lucid upgrade - a catalogue of disaster and pain</title>
          <pubDate>Fri, 14 May 2010 10:37:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2010/05/14/ubuntu-lucid-upgrade-a-catalogue-of-disaster-and-pain/</link>
          <guid>https://artificialworlds.net/blog/2010/05/14/ubuntu-lucid-upgrade-a-catalogue-of-disaster-and-pain/</guid>
          <description xml:base="https://artificialworlds.net/blog/2010/05/14/ubuntu-lucid-upgrade-a-catalogue-of-disaster-and-pain/">&lt;p&gt;I recently upgraded 3 machines to Lucid. I don&#x27;t recommend it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;broken-after-upgrade&quot;&gt;Broken after upgrade&lt;&#x2F;h3&gt;
&lt;p&gt;2 out of 3 machines did not boot after upgrade to Lucid.&lt;&#x2F;p&gt;
&lt;p&gt;One, an upgrade from Hardy, crashed hard during the upgrade (the screen was frozen with a prompt about lmsensors visible). It&#x27;s possible this was caused by a hardware issue of some kind, but it&#x27;s behaved itself since. When I powered off the machine it failed to boot because grub couldn&#x27;t see my hard drive. I believe this was because the file system had been changed in some way (to ext4?) but grub hadn&#x27;t been upgraded because of the crash, so it couldn&#x27;t handle it.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I spent a day trying to recover the system, and then a night rebuilding from scratch. I&#x27;m still configuring the system and regretting the upgrade.&lt;&#x2F;p&gt;
&lt;p&gt;The second, a netbook with a fairly standard Karmic install on it, upgraded smoothly and worked.&lt;&#x2F;p&gt;
&lt;p&gt;The third, my work machine, upgraded smoothly from Karmic, and frigged my fstab so my second drive wasn&#x27;t found. It changed my main drive to be identified by a UUID (instead of being &#x2F;dev&#x2F;sda), and this meant my second drive, which used to be &#x2F;dev&#x2F;sdb was now called &#x2F;dev&#x2F;sda. The upgrade failed to correct this in my fstab and gave me a very rude error during boot.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately I had the technical knowledge and found the time to fix these things, but it&#x27;s hardly something I&#x27;d like someone like my parents to have to deal with.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;annoying-after-upgrade&quot;&gt;Annoying after upgrade&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;My volume applet disappeared. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;gnome-media&#x2F;+bug&#x2F;552221&quot;&gt;Bug 552221&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Empathy doesn&#x27;t tell me about messages any more. When I enable the stupid indicator applet, I get a tiny yellow star when a new message has arrived. I can&#x27;t get an Empathy applet to appear in the status area.&lt;&#x2F;li&gt;
&lt;li&gt;Thunderbird 3 thrashed my disk and network for a couple of days to download and index all my IMAP mail. It is faster now, though.&lt;&#x2F;li&gt;
&lt;li&gt;I can&#x27;t get Thunderbird to give me a &quot;Calendar&quot; link in the bottom left to switch to the Lightning plugin. I did find the &quot;LightningButton&quot; add-on that allows me to have a button on the toolbar. Yes, you are reading that correctly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I loved Karmic. It got me all excited about Ubuntu again. I wish I&#x27;d stuck with it.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Gerry
    &lt;span class=&quot;comment_date&quot;&gt;2010-05-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wish I’d read this before, instead of later. My Karmic laptop upgrade went fine. But my desktop from Hardy to Lucid… enough dpkg dependency errors that the system crashed hard. Unless I find some shortcut, it’ll have to be rebuilt. It’s still ext3 and grub is intact and the old images, but it apparently changed enough that it’s a mess. They supplied no clue on how to approach fixing this. The abort requested I report a but against upgrade-manager, but launchpad doesn’t have that package. And this one’s my work machine for app development, so it’s pretty loaded.&lt;&#x2F;p&gt;
&lt;p&gt;Looks like smash and burn’s the only way, so I’m downloading lucid to a flash drive. At least the drives can still be mounted:–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2010-05-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Gerry, good luck with the recovery.  I wonder whether some kind of dependency problem caused my crash.  By the time I got back to it it was just frozen solid.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2010-05-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks to a post by Will McGugan, I fixed the problems I was having with the flash plugin failing and losing sound: &lt;a href=&quot;http:&#x2F;&#x2F;www.willmcgugan.com&#x2F;blog&#x2F;tech&#x2F;2010&#x2F;5&#x2F;2&#x2F;no-audio-in-flash-on-ubuntu-1004&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.willmcgugan.com&#x2F;blog&#x2F;tech&#x2F;2010&#x2F;5&#x2F;2...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(Summary: rm ~&#x2F;.mozilla&#x2F;plugins&#x2F;libflashplayer.so)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10.11</title>
          <pubDate>Tue, 24 Nov 2009 06:58:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/11/24/freeguide-0-10-11/</link>
          <guid>https://artificialworlds.net/blog/2009/11/24/freeguide-0-10-11/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/11/24/freeguide-0-10-11/">&lt;p&gt;I&#x27;ve made a new release of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt; today, which contains minor bug fixes.&lt;&#x2F;p&gt;
&lt;p&gt;I changed the stability rating on sourceforge to &quot;Production&#x2F;Stable&quot; (previously it was &quot;Alpha&quot;). Maybe I should bump the version number next time to 1.0. Certainly all this 0.10.x stuff is crazy.&lt;&#x2F;p&gt;
&lt;p&gt;I managed to get rid of quite a bit of unused and unworking code this release, so I&#x27;m feeling somewhat more positive about developing FreeGuide. My next plan is to spruce up the ant build so you can easily use it in Eclipse, and convert the whole lot to a single JAR, which may improve startup time.&lt;&#x2F;p&gt;
&lt;p&gt;The program seems to work for quite a few people, and I don&#x27;t change it much these days, so it might be time to call it 1.0. The only major feature I am planning (one day...) is to combine the Find dialog with the vertical view and possibly the favourites editor to make them different views onto the same underlying implementation. I&#x27;d like to make all of them show filter results as you type.&lt;&#x2F;p&gt;
&lt;p&gt;Other than that all I really want to do it fix user pain in terms of bugs and UI wrinkles.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Don&#x27;t design for performance until it&#x27;s too late</title>
          <pubDate>Fri, 20 Nov 2009 06:16:40 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/11/20/dont-design-for-performance-until-its-too-late/</link>
          <guid>https://artificialworlds.net/blog/2009/11/20/dont-design-for-performance-until-its-too-late/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/11/20/dont-design-for-performance-until-its-too-late/">&lt;p&gt;There is a piece of ancient wisdom which states:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikiquote.org&#x2F;wiki&#x2F;Donald_Knuth&quot;&gt;Premature optimisation is the root of all evil&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This ancient wisdom is, like all ancient wisdom, correct.&lt;&#x2F;p&gt;
&lt;p&gt;However.&lt;&#x2F;p&gt;
&lt;p&gt;It appears to have been reinterpreted as essentially meaning:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Don&#x27;t design for performance until it&#x27;s too late&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;which is clearly, and very importantly, very wrong.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;performance-is-a-feature&quot;&gt;Performance is a feature&lt;&#x2F;h3&gt;
&lt;p&gt;Before I begin I want us all to agree that &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;blog.stackoverflow.com&#x2F;2009&#x2F;08&#x2F;a-few-speed-improvements&#x2F;&quot;&gt;performance is a feature&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I work on a real-life &quot;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.subtraction.com&#x2F;2007&#x2F;10&#x2F;19&#x2F;if-it-looks-&quot;&gt;enterprise&lt;&#x2F;a&gt;&quot; application. Its features are entirely driven by the need for immediate cash, not by developers following pipe dreams. And yet, for the last 6-12 months the majority of my time has been spent trying to retrofit performance into this application. Believe me, this is not because we have users who are obsessive about wasting valuable seconds - it&#x27;s because our performance sucks so hard it&#x27;s deeply embarrassing.&lt;&#x2F;p&gt;
&lt;p&gt;What is your favourite program? How well does it perform? What is your least favourite? Why?&lt;&#x2F;p&gt;
&lt;p&gt;For me, and many other people, their answers to those questions demonstrate the importance of performance. Firefox was launched to improve the performance of Mozilla. People love git because of how fast it is. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lotusnotessucks.4t.com&#x2F;&quot;&gt;Lotus Notes is hated so much&lt;&#x2F;a&gt; partly because of its performance. My main complaints about programs I use involve performance (e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugzilla.mozilla.org&#x2F;show_bug.cgi?id=435153&quot;&gt;Thunderbird is too slow for IMAP email&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;A fast response to the user is one of those &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.joelonsoftware.com&#x2F;items&#x2F;2007&#x2F;06&#x2F;07.html&quot;&gt;crucial inches&lt;&#x2F;a&gt; on the journey to software that makes people happy. Making people happy gives you the kind of scary fanboyism that surrounds git. Wouldn&#x27;t you like that for your product?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-is-optimisation&quot;&gt;What is optimisation?&lt;&#x2F;h3&gt;
&lt;p&gt;When my hero said that premature optimisation was the root of all evil, he was talking in the days when you had to hand-optimise your C in assembly language. Or, more likely in his case, you had to hand-optimise your assembly language into faster assembly language. Optimisation like that very often obfuscates your code.&lt;&#x2F;p&gt;
&lt;p&gt;These days, 99% of the time, your compiler does all of this work for you, so you can have relatively comprehensible code in whatever trendy language you like, and still have the fastest possible local implementation of that in machine code.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, Knuth knew that 99% of your code is simply not performance-critical - it only runs a few times, or it&#x27;s just so much faster than some other bit that it doesn&#x27;t matter. The lesson we all learn eventually is that the slow bit is never quite what you thought, and you have to measure to find out where to concentrate your effort.&lt;&#x2F;p&gt;
&lt;p&gt;So, if optimisation is obfuscation, and 99% of your code isn&#x27;t the bit you need to make faster, it becomes clear that premature optimisation is the root of much evil.&lt;&#x2F;p&gt;
&lt;p&gt;But optimisation is not designing for performance.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;design-for-performance&quot;&gt;Design for performance&lt;&#x2F;h3&gt;
&lt;p&gt;Fundamentally, to get good performance, you are going to need to measure the time spent in various parts of your code (I suggest &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.codersnotes.com&#x2F;sleepy&quot;&gt;Very Sleepy&lt;&#x2F;a&gt; if you&#x27;re on Windows) and make the slow bits faster (or happen less often). However, there are still some principles you can follow that will mean you need to spend less time doing this*.&lt;&#x2F;p&gt;
&lt;p&gt;*Which is a pity, because I really love doing it.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t design for performance you are almost certainly going to need to restructure large parts of your program later, which is very difficult and time-consuming.&lt;&#x2F;p&gt;
&lt;p&gt;There are two aspects to designing for performance: writing good local code, and creating good global structure.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;write-good-local-code&quot;&gt;Write good local code&lt;&#x2F;h4&gt;
&lt;p&gt;Before you write an algorithm, think for a few minutes about how to make it work efficiently. e.g. if you&#x27;re writing C++, consider whether a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sgi.com&#x2F;tech&#x2F;stl&#x2F;Deque.html&quot;&gt;deque&lt;&#x2F;a&gt; or a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sgi.com&#x2F;tech&#x2F;stl&#x2F;List.html&quot;&gt;list&lt;&#x2F;a&gt; would be better than a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sgi.com&#x2F;tech&#x2F;stl&#x2F;Vector.html&quot;&gt;vector&lt;&#x2F;a&gt; for how you&#x27;re going to use it.&lt;&#x2F;p&gt;
&lt;p&gt;Think about what is idiomatic for your language and why. Think about what the computer really has to do to produce the results you are asking for. Are there going to be a lot of objects about? Maybe you can avoid copying them too many times. Are you creating and deleting a lot of objects? Can you reuse some instead? (Exercise caution with that one, though - if you start obfuscating you come into conflict with the ancient wisdom.)&lt;&#x2F;p&gt;
&lt;p&gt;Often, if you think through what you are doing, and the most efficient way to do it, you will end up with a faster and more memory-efficient algorithm, that expresses your intention &lt;em&gt;better&lt;&#x2F;em&gt; than if you&#x27;d written the first thing that came into your head. There is no downside to that.&lt;&#x2F;p&gt;
&lt;p&gt;Try to minimise the number of times you need to ask the operating system for a chunk of memory: this is surprisingly slow. E.g. in C++, prefer creating by-value data members instead of pointers to objects allocated with their own call to new. By the way, don&#x27;t worry if this sounds intimidating. The way to learn this stuff is to measure what you have done and then work out why it is slow. Next time you&#x27;ll jump straight to the fast solution without the detour to the slow one. Of course, none of this will matter if you don&#x27;t have good global structure.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;create-good-global-structure&quot;&gt;Create good global structure&lt;&#x2F;h4&gt;
&lt;p&gt;The hardest and most important work you need to do to have good performance is to have good structure in the ways the different parts of your program interact. This means thinking about how classes and components communicate with and control each other. It may be helpful to use a streaming style of communication - can you send little chunks of information to be processed one by one instead of a huge great blob? Try to make sure your components to use a common infrastructure: if different parts use different string classes you are going to spend most of your time copying from one to the other. The hardest and deepest mystery in getting good performance (and in programming generally) is choosing the right fundamental data structures. I&#x27;ll never forget the lesson I learnt** when a friend of mine had a conversation with me about a toy project I was doing (that was particularly focussed on trying to be fast) and then went away and produced code that was orders of magnitude faster, simply because he had chosen the right data structure. **The lesson I learnt was that I am not as good as I think I am. To be honest this section is a little shorter than I&#x27;d like because I know I don&#x27;t have a lot of answers about how to do this well. I do know, though, that if you don&#x27;t think about it now you will have the pain of restructuring your program later, when it&#x27;s full of bug fixes that are going to get rebroken by the restructuring. Of course, if you do think about it now you&#x27;re still pretty likely to need to change it later...&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ancient-wisdom&quot;&gt;Ancient wisdom&lt;&#x2F;h3&gt;
&lt;p&gt;Ancient wisdom is usually right, but misinterpreting it and using it as a license to write bad code is a bad idea. Carry on.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anatomy of an interpreter: the Lexer – Andy Balaam&amp;#x27;s Blog
    &lt;span class=&quot;comment_date&quot;&gt;2018-06-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] a more general programming point, following on from comments I made in a previous post, it is worth noting that the structure of the lexer (and, as we will see later, the parser) uses a […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Technorati appear to have broken openid delegation</title>
          <pubDate>Tue, 03 Nov 2009 12:08:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/11/03/technorati-appear-to-have-broken-openid-delegation/</link>
          <guid>https://artificialworlds.net/blog/2009/11/03/technorati-appear-to-have-broken-openid-delegation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/11/03/technorati-appear-to-have-broken-openid-delegation/">&lt;p&gt;I have just changed to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.myopenid.com&quot;&gt;myopenid&lt;&#x2F;a&gt; and can delegate to them from my own openid URL by following the instructions here: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.myopenid.com&#x2F;help#own_domain&quot;&gt;https:&#x2F;&#x2F;www.myopenid.com&#x2F;help#own_domain&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Google don&#x27;t appear to let you delegate to them (i.e. have your own url).&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m really glad I chose to set up a delegate now, since the brokenness of Technorati would have screwed me if I hadn&#x27;t done that.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>NNDB 0.1</title>
          <pubDate>Fri, 23 Oct 2009 20:57:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/10/23/nndb-0-1/</link>
          <guid>https://artificialworlds.net/blog/2009/10/23/nndb-0-1/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/10/23/nndb-0-1/">&lt;p&gt;I&#x27;ve managed to get &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;NNDB&quot;&gt;NNDB&lt;&#x2F;a&gt;, my C++ data storage library which is almost, but not entirely unlike SQL, into a fit state for a release.&lt;&#x2F;p&gt;
&lt;p&gt;You can create tables, set indices on columns, insert data, retrieve data using something like a SELECT, filter it using something like WHERE (which uses indices where available), and order it using something like an ORDER BY.&lt;&#x2F;p&gt;
&lt;p&gt;So far it has been a fantastic way to get my hands dirty with some &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Template_metaprogramming&quot;&gt;Template metaprogramming&lt;&#x2F;a&gt;, and some C++ as it should be*, but the reason why I started this was to help me think about how databases work, so I&#x27;m really looking forward to getting into how to implement JOINs. At the moment I have only very vague ideas.&lt;&#x2F;p&gt;
&lt;p&gt;NNDB is based heavily on the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sgi.com&#x2F;tech&#x2F;stl&#x2F;&quot;&gt;STL&lt;&#x2F;a&gt; (part of C++&#x27;s standard library), &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.boost.org&#x2F;&quot;&gt;BOOST&lt;&#x2F;a&gt; (a playground for things that might one day be in C++&#x27;s standard library, and hang-out for some of the cleverest people alive), and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;loki-lib.sourceforge.net&#x2F;&quot;&gt;Loki&lt;&#x2F;a&gt; (the continuation of Andrei Alexandrescu&#x27;s Template metaprogramming (but used for good, not evil?) library written for and explained in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.amazon.co.uk&#x2F;Modern-Design-Applied-Generic-Patterns&#x2F;dp&#x2F;0201704315&quot;&gt;Modern C++ Design&lt;&#x2F;a&gt;. This book ranks in the top 5 most exciting books I have read). I continue to be more impressed by all three the more I learn.&lt;&#x2F;p&gt;
&lt;p&gt;I have even been having &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sourceforge.net&#x2F;mailarchive&#x2F;forum.php?forum_name=loki-lib-general&amp;amp;max_rows=100&amp;amp;style=nested&amp;amp;viewmonth=200910&quot;&gt;discussions with the Loki devs&lt;&#x2F;a&gt; about some code I needed for NNDB that I think might be helpful for other people using Loki. It&#x27;s called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;nndb.git.sourceforge.net&#x2F;git&#x2F;gitweb.cgi?p=nndb&#x2F;nndb;a=blob;f=nndb&#x2F;ForEachType.h&quot;&gt;ForEachType&lt;&#x2F;a&gt; and it allows you to loop (at runtime) through all the types in a Typelist and do something for each one.&lt;&#x2F;p&gt;
&lt;p&gt;The project is already working in terms of helping me think about databases. For example, I really hadn&#x27;t thought before about how expensive ORDER BY is. To implement it I needed to create a temporary std::map covering the entire result set - in a real database this obviously requires reading every single row before we can even begin to return any results. The way to avoid this is to have an index. Which reminds me: the next thing I need to do is make ORDER BY able to use indices (at the moment it&#x27;s only WHEREs that take advantage of them).&lt;&#x2F;p&gt;
&lt;p&gt;So next on my list are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ORDER By uses indices&lt;&#x2F;li&gt;
&lt;li&gt;Non-unique indices (presumably implemented with a std::multimap)&lt;&#x2F;li&gt;
&lt;li&gt;Joins&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I am still very excited so you may see more releases over the next few months.&lt;&#x2F;p&gt;
&lt;p&gt;[* NNDB so far contains zero (0) calls to new and zero (0) calls to delete. Obviously the code it uses (e.g. std::vector) calls them, but that code manages all the memory for me, and most of it uses custom allocators to make it very fast. I have no idea how fast NNDB is, but maybe it could be quite fast. I am pretty confident it doesn&#x27;t contain any memory errors. Famous last words...]&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anonymous
    &lt;span class=&quot;comment_date&quot;&gt;2010-02-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You mention in your article that you need TypelistForEach, which (if I understood correctly), doesn’t exist in Loki yet; did you have a look at Boost.MPL (MetaProgramming Library)? It proposes a huge set of type containers along with several algorithms to operate on them at compile-time, such as transform, sort, fold and so on.&lt;&#x2F;p&gt;
&lt;p&gt;You could also check out Boost.Fusion, a library making the link between pure compile-time computations (cf. MPL) and pure runtime computations (cf. STL). It contains several heterogeneous containers that can be manipulated either at compile-time with metafunctions or at runtime with &quot;classic&quot; functions.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2010-02-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi, I haven’t looked at MPL yet – I only discovered it recently.  Fusion also sounds very interesting – thanks!&lt;&#x2F;p&gt;
&lt;p&gt;By the way, I submitted my code and it was accepted into Loki: &lt;a href=&quot;http:&#x2F;&#x2F;loki-lib.svn.sourceforge.net&#x2F;viewvc&#x2F;loki-lib&#x2F;trunk&#x2F;include&#x2F;loki&#x2F;ForEachType.h?view=markup&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;loki-lib.svn.sourceforge.net&#x2F;viewvc&#x2F;lo...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>NNDB&#x27;s Not a Database</title>
          <pubDate>Sat, 10 Oct 2009 08:22:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/10/10/nndbs-not-a-database/</link>
          <guid>https://artificialworlds.net/blog/2009/10/10/nndbs-not-a-database/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/10/10/nndbs-not-a-database/">&lt;p&gt;My latest project is called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;NNDB&quot;&gt;NNDB&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve worked with databases for quite a long time now, and for a while I&#x27;ve been thinking about how they work under the hood. I know very little about it, but I thought I could learn a bit by trying to implement something similar myself.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m interested in how queries work against joined tables, how to implement indices and so on.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve also been feeling that I want to do some C++ as an open source project. I do it all day at work, and for some problems it feels like the right tool for the job.&lt;&#x2F;p&gt;
&lt;p&gt;NNDB is sort-of like an in-memory database, but it works with C++ types for its columns, instead of a fixed set like varchar, int etc. You can put your own value-typed classes in the columns, and all values are type-checked at compile time.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s always struck me as strange that with a traditional code+SQL setup you have to keep your SQL in sync with your code manually. Of course, there are lots of trendy Object-Relational-Mapping thingies that solve that problem, but I felt it could be approached from another direction: instead of generating code to match your data, or generating SQL to match your code, why not specify your data structure &lt;em&gt;in&lt;&#x2F;em&gt; code?&lt;&#x2F;p&gt;
&lt;p&gt;In NNDB you define a table something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;typedef nndb::Values&amp;lt; unsigned long, std::string, std::string, MyDate &amp;gt;
    PersonValues;

class PersonTable : public nndb::Table&lt;personvalues&gt;
{
public:
    enum Columns
    {
        id,
        first_name,
        last_name,
        date_of_birth
    };
};&lt;&#x2F;personvalues&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Actually, defining your own class is unnecessary, but it&#x27;s nice to have an enum to name your columns, and making a class gives you a nice place to put it.&lt;&#x2F;p&gt;
&lt;p&gt;To insert a row you do something like this:&lt;&#x2F;p&gt;
&lt;pre&gt;PersonTable person_table;
person_table.Insert( PersonValues( 0,
    &quot;Andy&quot;, &quot;Balaam&quot;, MyDate( 12000000 ) ) );&lt;&#x2F;pre&gt;
&lt;p&gt;You can do simple queries with WHERE and ORDER BY clauses, and I&#x27;m working on indexes.&lt;&#x2F;p&gt;
&lt;p&gt;After that will come JOINs, and anything else that takes my fancy.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t anticipate NNDB being useful to anyone - it&#x27;s really for me to understand why things are as they are in the world of databases. However, you never know - it may turn out to be a fast and convenient way to store data in the C++ world. I think some of the applications that use databases don&#x27;t really need the kind of concurrent multi-user network-accessible features they have, but really just want to search, join and store reliably, and NNDB might one day grow into something that can find a niche.&lt;&#x2F;p&gt;
&lt;p&gt;To explore more, check out the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;nndb.git.sourceforge.net&#x2F;git&#x2F;gitweb.cgi?p=nndb&#x2F;nndb;a=blob;f=test&#x2F;complete_sample.cpp;h=438552aea1b15343be1ad220c5f6240b17b35ce3;hb=HEAD&quot;&gt;complete example&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    SG
    &lt;span class=&quot;comment_date&quot;&gt;2009-10-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;instead of generating code to match your data, or generating SQL to match your code, why not specify your data structure in code?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;See LISP :)&lt;&#x2F;p&gt;
&lt;p&gt;PS: Really cool C++ project!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2009-10-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks!  I am actually currently reading &lt;a href=&quot;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;book.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;mitpress.mit.edu&#x2F;sicp&#x2F;full-text&#x2F;book&#x2F;b...&lt;&#x2F;a&gt; and loving it.  It turns out pretty much all the interesting stuff in template metaprogramming is almost identical to how you would do the same thing in LISP.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Guillaume Yziquel
    &lt;span class=&quot;comment_date&quot;&gt;2010-02-09&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Two projects that might be interesting to you, though quite far from C++ userland.&lt;&#x2F;p&gt;
&lt;p&gt;PG’OCamlm by Richard Jones: &lt;a href=&quot;http:&#x2F;&#x2F;pgocaml.berlios.de&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;pgocaml.berlios.de&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A statically typed relational algebra by Mauricio Fernandez:&lt;br&#x2F;&gt;
&lt;a href=&quot;http:&#x2F;&#x2F;eigenclass.org&#x2F;hiki&#x2F;addressing-orm-problem-typed-relational-algebra&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;eigenclass.org&#x2F;hiki&#x2F;addressing-orm-pro...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2010-02-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Guillaume – really interesting projects, and further really interesting links at the bottom of the second one.  I knew someone else must have done something like this before…&lt;&#x2F;p&gt;
&lt;p&gt;In self-augmenting language it is possible to build something like this on top of a real database, which I think would be very difficult in C++.&lt;&#x2F;p&gt;
&lt;p&gt;I have been having thoughts about what my ideal language would be.  Recently I have been wondering about whether it’s possible to create a self-augmenting language that is also compiled in a traditional way, and type-safe in the C++ sense.  Essentially I want to be able to extend the C++ compiler at compile time.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Diffident 0.3</title>
          <pubDate>Sat, 10 Oct 2009 07:54:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/10/10/diffident-0-3/</link>
          <guid>https://artificialworlds.net/blog/2009/10/10/diffident-0-3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/10/10/diffident-0-3/">&lt;p&gt;My original plan for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Diffident&quot;&gt;Diffident&lt;&#x2F;a&gt;, the side-by-side diff viewer and editor that works in a terminal, was to implement basic editing capabilities before making another release.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, that turned out to be quite ambitious. It involves essentially implementing a full text editor, which is not really what I want to do. I may actually implement a &quot;jump out to $EDITOR&quot; option before the basic text editing facilities.&lt;&#x2F;p&gt;
&lt;p&gt;What I have implemented for this release is the ability to add and remove lines, and copy lines from one side to the other. For my personal use, this covers about 90% of cases, so I think it&#x27;s worthy of a release.&lt;&#x2F;p&gt;
&lt;p&gt;There is no undo&#x2F;redo as yet, but the framework for that is in place, so I may make another release sometime soonish that is just for that.&lt;&#x2F;p&gt;
&lt;p&gt;So the dream of a diff viewer &lt;em&gt;and editor&lt;&#x2F;em&gt; is starting to come true...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>CBeebies and other channels not working with mplayer</title>
          <pubDate>Fri, 09 Oct 2009 16:08:03 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/10/09/cbeebies-and-other-channels-not-working-with-mplayer/</link>
          <guid>https://artificialworlds.net/blog/2009/10/09/cbeebies-and-other-channels-not-working-with-mplayer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/10/09/cbeebies-and-other-channels-not-working-with-mplayer/">&lt;p&gt;It turns out the scan command from the linuxtv-dvb-apps package (on Ubuntu Hardy Heron, anyway) isn&#x27;t writing the correct channels.conf, and this makes some channels not work for me, including CBeebies, CBBC Channel and CITV.&lt;&#x2F;p&gt;
&lt;p&gt;The fix is to do as this person did: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ubuntuforums.org&#x2F;showthread.php?t=686829&quot;&gt;http:&#x2F;&#x2F;ubuntuforums.org&#x2F;showthread.php?t=686829&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Basically, the kaffeine TV viewer scans the channels fine so you can manually copy the values you need from its config file.&lt;&#x2F;p&gt;
&lt;p&gt;The incorrect channels have &quot;0:0&quot; as the second-last and third-last numbers in their row in ~&#x2F;.mplayer&#x2F;channels.conf .&lt;&#x2F;p&gt;
&lt;p&gt;Those two zeros should be replaced by the first two numbers you find in the row for this channel in ~&#x2F;.kde&#x2F;share&#x2F;apps&#x2F;kaffeine&#x2F;channels.dvb . You can ignore the numbers in brackets in this file.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Firefox keyword search for finding C++ keywords</title>
          <pubDate>Wed, 23 Sep 2009 09:58:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/09/23/firefox-keyword-search-for-finding-c-keywords/</link>
          <guid>https://artificialworlds.net/blog/2009/09/23/firefox-keyword-search-for-finding-c-keywords/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/09/23/firefox-keyword-search-for-finding-c-keywords/">&lt;p&gt;I often want to search the SGI C++ reference for a keyword. The best way I have found to jump straight to the page I want is to use Google&#x27;s &quot;I&#x27;m Feeling Lucky&quot; search limited to searching within sgi.com.&lt;&#x2F;p&gt;
&lt;p&gt;You can create a Firefox keyword search to allow you to do this quickly from the location bar. Now I just type Ctrl-L then e.g. &quot;c vector&quot; to jump straight to the page about std::vector.&lt;&#x2F;p&gt;
&lt;p&gt;To do this, make a bookmark (to anything) and then right-click it in the Bookmarks menu and choose Properties. Edit it to look like this:&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;cppsearch.png&quot; alt=&quot;Firefox C++ search keyword bookmark&quot; title=&quot;cppsearch&quot; width=&quot;539&quot; height=&quot;254&quot; class=&quot;size-full wp-image-226&quot;&gt;
&lt;p&gt;The Location field is set to http:&#x2F;&#x2F;www.google.com&#x2F;search?as_q=%s&amp;amp;as_sitesearch=sgi.com&amp;amp;btnI=1, and the Keyword field is just c.&lt;&#x2F;p&gt;
&lt;p&gt;This post was largely for my own benefit to remember this next time I need to set it up, but I thought it might be useful to others as the information about how to set these searches up is not easy to find.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to go to a google search results page, instead of jumping to the &quot;I&#x27;m Feeling Lucky&quot; result, remove the &amp;amp;btnI=1 part.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-23&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Strangely, I still find my hardcopy of ISO&#x2F;IEC&#x2F;BS 14882:2003 the most convenient reference for C++. Ctrl-F in the pdf version at work also works quite well but is somehow not as satisfyingly tactile.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Flaws in the software patents system</title>
          <pubDate>Thu, 10 Sep 2009 12:56:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/09/10/flaws-in-the-software-patents-system/</link>
          <guid>https://artificialworlds.net/blog/2009/09/10/flaws-in-the-software-patents-system/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/09/10/flaws-in-the-software-patents-system/">&lt;p&gt;&lt;em&gt;Note: I am in favour of patents on inventions.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I believe the current software patents system has a number of flaws (in decreasing order of importance to me):&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;They attempt to close off the use of knowledge rather than open it up. If we were not filing patents for our work, we could be writing papers or books to share that knowledge. What would have happened if the Gang of Four had filed patent applications instead of writing &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Design_Patterns_%28book%29&quot;&gt;Design Patterns&lt;&#x2F;a&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;They can verge on the dishonest. Describing most software development work as &quot;novel&quot; and &quot;non-obvious&quot; is simply incorrect. There is a huge community of software developers, and the idea that none of them would find what I am doing &quot;obvious&quot; beggars belief.&lt;&#x2F;li&gt;
&lt;li&gt;They are a tool that big companies can use to squash small companies. Small companies can&#x27;t afford to develop a large defensive patent portfolio. Patents don&#x27;t even need to be valid or high-quality: if there are enough of them, a company that can afford a lot of lawyers can destroy one that can&#x27;t.&lt;&#x2F;li&gt;
&lt;li&gt;They are a tool that big companies can use to squash open source projects. Many open source projects have no money at all. If a large company doesn&#x27;t like some project, they can simply sue for patent infringement (even for patents that are blatantly irrelevant) and the project will be toast.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;So I don&#x27;t want to be involved in patent applications at my work, even though my employer is keen on patents.&lt;&#x2F;p&gt;
&lt;p&gt;I am now considering how I should act on this opinion. I don&#x27;t want to screw my colleagues but I think I want to refuse to be a part of any patent applications as far as I can do that without screwing them.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>IGCC - a real-eval-print loop for C&#x2F;C++</title>
          <pubDate>Mon, 31 Aug 2009 02:56:50 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/08/31/igcc-a-real-eval-print-loop-for-cc/</link>
          <guid>https://artificialworlds.net/blog/2009/08/31/igcc-a-real-eval-print-loop-for-cc/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/08/31/igcc-a-real-eval-print-loop-for-cc/">&lt;p&gt;When you first hear about the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;REPL&quot;&gt;Read-Eval-Print Loop&lt;&#x2F;a&gt; you might well think &quot;So what?&quot; as I did.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s so great about being able to type commands interactively?&lt;&#x2F;p&gt;
&lt;p&gt;But the thing is that it creeps up on you.&lt;&#x2F;p&gt;
&lt;p&gt;Everyone already knows programming is an interactive thing - we need constant feedback to validate our ideas. Programming on paper is incredibly frustrating because you have to plough on with assumptions that are probably wrong.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s just so comfortable to be able to try out ideas in an interactive interpreter.&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;Baby_foot_artlibre_jnl.jpg&quot; alt=&quot;Foosball&quot; title=&quot;Foosball&quot; width=&quot;150&quot; height=&quot;200&quot; class=&quot;alignright size-full wp-image-200&quot;&gt;
&lt;p&gt;I mean, it&#x27;s really not much hassle to create a new directory, make a new file, edit the file to contain the code you want to try, remember the right command to compile it, then run the program and see the results, is it?&lt;&#x2F;p&gt;
&lt;p&gt;Well, no, it isn&#x27;t, but it&#x27;s enough of a hassle that sometimes you don&#x27;t bother and you try it out in the code you are really working on, and if your work is like mine that means a minimum of 5 minutes to compile and link, and there you are playing foosball again when you could be getting something done.&lt;&#x2F;p&gt;
&lt;p&gt;The REPL gives you a place to try throwaway things extremely quickly, and when you&#x27;re working with something beautiful like Python it&#x27;s easy to get addicted.&lt;&#x2F;p&gt;
&lt;p&gt;So my mind started to wander and it struck me that a pale imitation of the REPL could be made for us poor C++ programmers, and it would generally serve the purposes I&#x27;ve described above.&lt;&#x2F;p&gt;
&lt;p&gt;So &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;IGCC&#x2F;IGCC&quot;&gt;IGCC&lt;&#x2F;a&gt; was born. Its name means &quot;Interactive GCC&quot; and it&#x27;s a read-eval-print loop for C++ (and, for most cases it will work for C too).&lt;&#x2F;p&gt;
&lt;p&gt;It uses the real GCC underneath, so you know you are running the exact code you would be (and it&#x27;s somewhat easier to write than a custom C&#x2F;C++ interpreter) and all it does is take away the hassle of creating a simple program and compiling it with GCC.&lt;&#x2F;p&gt;
&lt;p&gt;It wraps your code in a standard C program, includes some common dependencies, and compiles it, printing the results of running them immediately. Using it looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ .&#x2F;igcc
g++&amp;gt; int a = 5;
g++&amp;gt; a += 2;
g++&amp;gt; cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
7
g++&amp;gt; --a;
g++&amp;gt; cout &amp;lt;&amp;lt; a &amp;lt;&amp;lt; endl;
6
g++&amp;gt;
&lt;&#x2F;pre&gt;
&lt;p&gt;Apart from all the sugar that I&#x27;d love to add, the main missing features are some kind of equivalent of the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.network-theory.co.uk&#x2F;docs&#x2F;pytut&#x2F;dirFunction.html&quot;&gt;Python dir command&lt;&#x2F;a&gt;, and code completion.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not rocket science, but it might make you a little bit more interactive in your C and C++ coding, which might save you valuable foosball time.&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy, improve, etc. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;IGCC&#x2F;IGCC&quot;&gt;IGCC&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Foosball image taken from http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;File:Baby_foot_artlibre_jnl.jpg&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Diffident - command line side-by-side diff editor</title>
          <pubDate>Wed, 26 Aug 2009 20:13:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/08/26/diffident-command-line-side-by-side-diff-editor/</link>
          <guid>https://artificialworlds.net/blog/2009/08/26/diffident-command-line-side-by-side-diff-editor/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/08/26/diffident-command-line-side-by-side-diff-editor/">&lt;p&gt;I really like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.scootersoftware.com&#x2F;&quot;&gt;Beyond Compare&lt;&#x2F;a&gt;, which is a proprietary diff program with all those little touches that make it a joy to use*. The way I write code at work generally involves a bit of hacking in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;jedit.org&quot;&gt;jEdit&lt;&#x2F;a&gt;, checking the code myself, and then reviewing the code with a colleague.&lt;&#x2F;p&gt;
&lt;p&gt;*Recently, though, they&#x27;ve brought out a newer version that seems to overcomplicate things.&lt;&#x2F;p&gt;
&lt;p&gt;Both my own checking and the code review with a colleague generally involve comparing the code with the previous version in the (perforce) repository. Beyond Compare integrates nicely with the perforce tools and allows you to see a change as diffs of each file involved.&lt;&#x2F;p&gt;
&lt;p&gt;None of this is anything new to the Free Software world, of course. All the version control programs I&#x27;ve used allow you to do the equivalent of &lt;code&gt;cvs diff&lt;&#x2F;code&gt; and see what changes you&#x27;ve made. &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;git-scm.com&#x2F;&quot;&gt;Git&lt;&#x2F;a&gt; has a particularly good &lt;code&gt;git diff&lt;&#x2F;code&gt; mode which by default colours your diff and pipes it to less, making it easy to read and use.&lt;&#x2F;p&gt;
&lt;p&gt;What I have found myself missing recently, though, is the ability to edit the files as you diff them. The whole point of reviewing what you&#x27;ve done is to make changes as they occur to you, and with perforce + Beyond Compare it is really natural to make those changes within the diff tool.&lt;&#x2F;p&gt;
&lt;p&gt;Incidentally, I also really like the side-by-side style of Beyond Compare. By default it inserts &quot;missing&quot; lines so that all the similar lines are aligned, rather than trying to indicate with balloon-like lines that code has been inserted or transformed. I find those balloons very annoying and confusing (and they take up space on my screen, grr).&lt;&#x2F;p&gt;
&lt;p&gt;Having a side-by-side view also makes copying lines from one side to the other much easier. I often copy from one side to the other - especially when I realise what I have done is stupid and I want to revert a section back to how it was.&lt;&#x2F;p&gt;
&lt;p&gt;So, to curtail an already-long story, I decided there was yet another area of my life where the only solution was to run software I had written, rather than relying on the shoddy stuff put out by others, so I wrote a diff tool.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Diffident&#x2F;Diffident&quot;&gt;Diffident&lt;&#x2F;a&gt; is a diff tool inspired by Beyond Compare and git. It shows you a side-by-side diff of your files in a terminal (one day it may have a GTK+ interface too) and allows you to edit them. The editing part is in development - expect a release fairly soon, or get the code from the git repository. The output is coloured, and you can see the whole of both files, using keyboard shortcuts to jump to the next and previous differences.&lt;&#x2F;p&gt;
&lt;p&gt;You might ask &quot;Why not just use Beyond Compare?&quot; For a number of reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;It is not Free Software. I can&#x27;t improve it or trust it.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s sort of Windows-y. I know there is a Linux version, but I bet it&#x27;s not very Linux-y. (Disclaimer: I&#x27;ve never tried it ;)&lt;&#x2F;li&gt;
&lt;li&gt;It doesn&#x27;t work in a terminal.&lt;&#x2F;li&gt;
&lt;li&gt;The inline editing support is not great. Its real strength is copying from side to side.&lt;&#x2F;li&gt;
&lt;li&gt;It doesn&#x27;t feel right when used with git. I have got this set up in my Cygwin environment - it works, but it&#x27;s no fun.&lt;&#x2F;li&gt;
&lt;li&gt;It isn&#x27;t written by me.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I&#x27;d really like Diffident to become the de-facto diff tool for git people (and everyone else). That proves to be a bit trickier than I&#x27;d like because of the way git interacts with diff tools, but I&#x27;ve got a decent solution for using it as the GIT_EXTERNAL_DIFF tool, and I hope to improve it in the future. (For those who are interested, the kind of thing I&#x27;m thinking about is how to get &lt;code&gt;git diff --cached&lt;&#x2F;code&gt; to allow me to edit the files in the index.)&lt;&#x2F;p&gt;
&lt;p&gt;So anyway, check out &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Diffident&#x2F;Diffident&quot;&gt;Diffident&lt;&#x2F;a&gt; and if you like it, help me make it better.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Random background in GNOME</title>
          <pubDate>Fri, 31 Jul 2009 08:50:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/07/31/random-background-in-gnome/</link>
          <guid>https://artificialworlds.net/blog/2009/07/31/random-background-in-gnome/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/07/31/random-background-in-gnome/">&lt;p&gt;I&#x27;ve written a little script to show a different random background in GNOME every day, by looking inside your Photos directory.&lt;&#x2F;p&gt;
&lt;p&gt;It will rotate them to be the right way up, if you have exiftran installed.&lt;&#x2F;p&gt;
&lt;p&gt;Get it here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;wiki&#x2F;Main&#x2F;RandomBackground&quot;&gt;Random Background&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Analog literals</title>
          <pubDate>Wed, 20 May 2009 09:17:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/05/20/analog-literals/</link>
          <guid>https://artificialworlds.net/blog/2009/05/20/analog-literals/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/05/20/analog-literals/">&lt;p&gt;I love this: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.xs4all.nl&#x2F;~weegen&#x2F;eelis&#x2F;analogliterals.xhtml&quot;&gt;C++ Multi-Dimensional Analog Literals&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I quote:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Have you ever felt that integer literals like &quot;4&quot; don&#x27;t convey the true size of the value they denote? If so, use an analog integer literal instead:&lt;pre&gt;unsigned int b = I---------I;&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It goes on to explain that you can use 2- and 3-dimensional &quot;analog literals&quot;. Genius. Read the article. Try to read the code :)&lt;&#x2F;p&gt;
&lt;p&gt;Isn&#x27;t C++ ... erm ... powerful?&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll notice that there are 9 dashes used to denote 4. This is because the trick it is using uses operator--. I&#x27;m sure the original author did this in his&#x2F;her sleep and thought it was too trivial to post (or posted it before?) but I thought: if we can use operator! instead, can&#x27;t we create analog literals that use the same number of symbols as the number we want?&lt;&#x2F;p&gt;
&lt;p&gt;The answer is yes, and it&#x27;s pretty simple:&lt;&#x2F;p&gt;
&lt;p&gt;notliterals.h:&lt;pre&gt;class NotLiteral
{
public:
NotLiteral( unsigned int ival )
: val_( ival )
{
}&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;NotLiteral operator!() const
{
	return NotLiteral( val_ + 1 );
}

operator unsigned int() const
{
	return val_;
}

unsigned int val_;
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;};&lt;&#x2F;p&gt;
&lt;p&gt;const NotLiteral NL( 0 );
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;test_notliterals.cpp:&lt;pre&gt;#include &quot;notliterals.h&quot;
#include &amp;lt;cassert&amp;gt;&lt;&#x2F;p&gt;
&lt;p&gt;int main()
{
assert( !!!!NL == 4 );
assert( !!NL == 2 );&lt;&#x2F;p&gt;
&lt;pre&gt;&lt;code&gt;assert( !!!!!!!!!!!!!!!NL == 15 );
&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;}
&lt;&#x2F;pre&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With this simpler form, it&#x27;s almost believable that there might be some kind of useful application?&lt;&#x2F;p&gt;
&lt;p&gt;Extending this to 3 dimensions is left as an exercise for the reader. For 2 dimensions, if you just want the area (not the width and height), how about this?:&lt;&#x2F;p&gt;
&lt;pre&gt;	assert( !!!
	        !!!
	        !!!NL == 9 );
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; By the way, if you don&#x27;t like all the emphasis! of! using! exclamation! marks! you can do the same thing with the unary one&#x27;s complement operator, ~. Just replace &quot;!&quot; everywhere above with &quot;~&quot; and you&#x27;re done. Unfortunately, you can&#x27;t do the same with - or + because the parser recognises &quot;--&quot; as the decrement operator instead of seeing that it is &lt;em&gt;clearly&lt;&#x2F;em&gt; two calls to the unary negation operator.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Separate regular expressions, or one more complex one?</title>
          <pubDate>Fri, 03 Apr 2009 06:44:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/04/03/separate-regular-expressions-or-one-more-complex-one/</link>
          <guid>https://artificialworlds.net/blog/2009/04/03/separate-regular-expressions-or-one-more-complex-one/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/04/03/separate-regular-expressions-or-one-more-complex-one/">&lt;p&gt;I have asked myself this question several times, so I thought it was about time I did a test and found an answer.&lt;&#x2F;p&gt;
&lt;p&gt;If the user of your program can supply you with a list of regular expressions to match against some text, should you combine those expressions into one big one, or treat them separately?&lt;&#x2F;p&gt;
&lt;p&gt;In my case I need an OR relationship, so combining them just means putting a pipe symbol between them.*&lt;&#x2F;p&gt;
&lt;p&gt;So: one expression made by ORing, or looping through several - which is better? &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Np6gyUb0E7o&quot;&gt;There&#x27;s only one way to find out&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre&gt;import re, sys

line_with_match_foo = &quot;This line contains foo.&quot;
line_with_match_baz = &quot;This line contains baz.&quot;
line_without_match = &quot;This line does not contain it.&quot;

re_strings = ( &quot;foo&quot;, &quot;bar1&quot;, &quot;bar2&quot;, &quot;baz&quot;, &quot;bar3&quot;, &quot;bar4&quot;, )

piped_re = re.compile( &quot;|&quot;.join( re_strings ) )

separate_res = list( re.compile( r ) for r in re_strings )

NUM_ITERATIONS = 1000000

def piped( line ):
    for i in range( NUM_ITERATIONS ):
        if piped_re.search( line ):
            print &quot;match!&quot; # do something

def separate( line ):
    for i in range( NUM_ITERATIONS ):
        for s in separate_res:
            if s.search( line ):
                print &quot;match!&quot; # do something
                break # stop looping because we matched

arg = sys.argv[1]

if arg == &quot;--piped-nomatch&quot;:
    piped( line_without_match )
elif arg == &quot;--piped-match-begin&quot;:
    piped( line_with_match_foo )
elif arg == &quot;--piped-match-middle&quot;:
    piped( line_with_match_baz )
elif arg == &quot;--separate-nomatch&quot;:
    separate( line_without_match )
elif arg == &quot;--separate-match-begin&quot;:
    separate( line_with_match_foo )
elif arg == &quot;--separate-match-middle&quot;:
    separate( line_with_match_baz )
&lt;&#x2F;pre&gt;
&lt;p&gt;And here are the results:&lt;&#x2F;p&gt;
&lt;pre&gt;$ time python re_timings.py --piped-nomatch &amp;gt; &#x2F;dev&#x2F;null

real    0m0.987s
user    0m0.943s
sys     0m0.032s
$ time python re_timings.py --separate-nomatch &amp;gt; &#x2F;dev&#x2F;null

real    0m3.695s
user    0m3.641s
sys     0m0.037s
&lt;&#x2F;pre&gt;
&lt;p&gt;So when no regular expressions match, the combined expression is 3.6 times faster.&lt;&#x2F;p&gt;
&lt;pre&gt;$ time python re_timings.py --piped-match-middle &amp;gt; &#x2F;dev&#x2F;null

real    0m1.900s
user    0m1.858s
sys     0m0.033s
$ time python re_timings.py --separate-match-middle &amp;gt; &#x2F;dev&#x2F;null

real    0m3.543s
user    0m3.439s
sys     0m0.042s
&lt;&#x2F;pre&gt;
&lt;p&gt;And when an expression near the middle of the list matches, the combined expression is 1.8 times faster.&lt;&#x2F;p&gt;
&lt;pre&gt;$ time python re_timings.py --piped-match-begin &amp;gt; &#x2F;dev&#x2F;null

real    0m1.847s
user    0m1.797s
sys     0m0.035s
$ time python re_timings.py --separate-match-begin &amp;gt; &#x2F;dev&#x2F;null

real    0m1.649s
user    0m1.597s
sys     0m0.032s
&lt;&#x2F;pre&gt;
&lt;p&gt;But in the (presumably much rarer) case where all lines match the first expression in the list, the separate expressions are marginally faster.&lt;&#x2F;p&gt;
&lt;p&gt;A clear win for combing the expressions, unless you think it&#x27;s likely that most lines will match expressions early in the list.&lt;&#x2F;p&gt;
&lt;p&gt;Note also if you combine the expressions the performance is similar when the matching expression is at different positions in the list (whereas in the other case list order matters a lot), so there is probably no need for you or your user to second-guess what order to put the expressions in, which makes life easier for everyone.&lt;&#x2F;p&gt;
&lt;p&gt;I would guess the results would be similar in other programming languages. I certainly found it to be similar in C# on .NET when I tried it a while ago.&lt;&#x2F;p&gt;
&lt;p&gt;By combining the expressions we ask the regular expression engine to do the heavy lifting for us, and it is specifically designed to be good at that job.&lt;&#x2F;p&gt;
&lt;p&gt;Open questions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Have I made a mistake that makes these results invalid?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;* Can arbitrary regular expressions be ORed together simply by concatenating them with a pipe symbol in between?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;Can we do something similar if the problem requires us to AND expressions?&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
          <title>New Baby</title>
          <pubDate>Sat, 21 Mar 2009 20:53:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/03/21/new-baby/</link>
          <guid>https://artificialworlds.net/blog/2009/03/21/new-baby/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/03/21/new-baby/">&lt;p&gt;Finally the number of people in our family is prime again.&lt;&#x2F;p&gt;
&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;img_0178.jpg&quot; alt=&quot;img_0178&quot; title=&quot;img_0178&quot; width=&quot;400&quot; height=&quot;279&quot; class=&quot;alignnone size-full wp-image-132&quot;&gt;
&lt;p&gt;Mum, baby, siblings etc. are all really well and enjoying ourselves.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Talk in code</title>
          <pubDate>Thu, 05 Mar 2009 10:31:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2009/03/05/talk-in-code/</link>
          <guid>https://artificialworlds.net/blog/2009/03/05/talk-in-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2009/03/05/talk-in-code/">&lt;p&gt;Last week we had an extended discussion at work about how we were going to implement a specific feature.&lt;&#x2F;p&gt;
&lt;p&gt;This discussion hijacked our entire &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.scrumalliance.org&#x2F;&quot;&gt;Scrum&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.scrumalliance.org&#x2F;pages&#x2F;scrum_ceremonies&quot;&gt;sprint planning meeting&lt;&#x2F;a&gt; (yes, I know, we should have &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Timebox&quot;&gt;time-boxed&lt;&#x2F;a&gt; it). It was painful, but the guy who was going to implement it (yes, I know, we should all collectively own our tasks) needed the discussion: otherwise it wasn&#x27;t going to get implemented. It certainly wasn&#x27;t going to get broken into short tasks until we knew how we were going to do it.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, asides aside, I came out of that discussion bruised but triumphant. We had a plan not only on how to write the code, but also how to test it. I believe the key thing that slowly led the discussion from a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Fear,_uncertainty_and_doubt&quot;&gt;FUD&lt;&#x2F;a&gt;-throwing contest into a constructive dialogue was the fact that we began to &lt;strong&gt;talk in code&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are two facets to this principle:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;1-show-me-the-code&quot;&gt;1. Show me the code&lt;&#x2F;h3&gt;
&lt;p&gt;As Linus once said, &quot;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lkml.org&#x2F;lkml&#x2F;2000&#x2F;8&#x2F;25&#x2F;132&quot;&gt;Talk is cheap. Show me the code.&lt;&#x2F;a&gt;&quot;.&lt;&#x2F;p&gt;
&lt;p&gt;If you are at all disagreeing about how what you&#x27;re doing will work, open up the source files in question. Write example code - modify the existing methods or sketch a new one. Outline the classes you will need. Code is inherently unambiguous. White board diagrams and hand-waving are not.&lt;&#x2F;p&gt;
&lt;p&gt;Why wouldn&#x27;t you do this? Fear you might be wrong? Perhaps you should have phrased your argument a little less strongly?&lt;&#x2F;p&gt;
&lt;p&gt;Is this slower than drawing boxes on a whiteboard? Not if you include time spent resolving the confusion caused by the ambiguities inherent in line drawings.&lt;&#x2F;p&gt;
&lt;p&gt;Does &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Unified_Modeling_Language&quot;&gt;UML&lt;&#x2F;a&gt; make whiteboards less ambiguous? Yes, if all your developers can be bothered to learn it. But why learn a new language when you can communicate using the language you all speak all day - code?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-create-a-formal-language-to-describe-the-problem&quot;&gt;2. Create a formal language to describe the problem&lt;&#x2F;h3&gt;
&lt;p&gt;If your problem is sufficiently complex, you may want to codify the problem into a formal (text-based) language.&lt;&#x2F;p&gt;
&lt;p&gt;In last week&#x27;s discussion we were constantly bouncing back and forth between different corner cases until we started writing them down in a formal language.&lt;&#x2F;p&gt;
&lt;p&gt;The language I chose was an adaptation of a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.martinfowler.com&#x2F;bliki&#x2F;DomainSpecificLanguage.html&quot;&gt;Domain-specific language&lt;&#x2F;a&gt; I wrote to test a different part of our program. I would love to turn the cases we wrote down in that meeting into real tests that run after every build (in fact I am working on it) but their immediate value was to turn very confusing &quot;what-if&quot;s into concrete cases we could discuss.&lt;&#x2F;p&gt;
&lt;p&gt;Before we started using the formal language, the conversations went something like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Developer:&lt;&#x2F;strong&gt; &quot;If we implement it like that, this bad thing will happen.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Manager:&lt;&#x2F;strong&gt; &quot;That&#x27;s fine - it&#x27;s a corner case that we can tidy up later if we need it.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Developer:&lt;&#x2F;strong&gt; (Muttering) &quot;He clearly doesn&#x27;t understand what I mean.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Repeat&lt;&#x2F;strong&gt;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After we started using the formal language they went something like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Developer:&lt;&#x2F;strong&gt; &quot;If we implement it like that, this bad thing will happen.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Me:&lt;&#x2F;strong&gt; &quot;Write it down, I tell you.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Developer:&lt;&#x2F;strong&gt; (Typing) &quot;See, this will happen!&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Manager:&lt;&#x2F;strong&gt; &quot;That&#x27;s fine - it&#x27;s a corner case that we can tidy up later if we need it.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Developer:&lt;&#x2F;strong&gt; (Muttering) &quot;Flipping managers.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;&#x2F;h3&gt;
&lt;p&gt;The conversation progresses if all parties believe the others understand what they are saying. It is not disagreement that paralyses conversations - it is misunderstanding.&lt;&#x2F;p&gt;
&lt;p&gt;To avoid misunderstanding, talk in code - preferably a real programming language, but if that&#x27;s too verbose, a text-based code that is unambiguous and understood by everyone involved.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;note-on-whiteboards&quot;&gt;Note on whiteboards&lt;&#x2F;h3&gt;
&lt;p&gt;You can&#x27;t copy and paste them, and you can&#x27;t (easily) keep what you did with them, and you can&#x27;t use them to communicate over long distances.&lt;&#x2F;p&gt;
&lt;p&gt;And don&#x27;t even try and suggest an electronic whiteboard. In a few years they may solve all of the above problems, but not now. They fail the &quot;can I draw stuff?&quot; test at the moment.&lt;&#x2F;p&gt;
&lt;p&gt;Even when electronic whiteboards solve those problems, they won&#x27;t solve the fact that lines and boxes are more ambiguous and less detailed than code in text form.&lt;&#x2F;p&gt;
&lt;p&gt;If you all know and like UML, that makes your diagrams less ambiguous, but still they often don&#x27;t allow enough detail: why bother?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Fixing the vertical panel window list on Ubuntu Hardy</title>
          <pubDate>Wed, 10 Dec 2008 22:20:49 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/12/10/fixing-the-vertical-panel-window-list-on-ubuntu-hardy/</link>
          <guid>https://artificialworlds.net/blog/2008/12/10/fixing-the-vertical-panel-window-list-on-ubuntu-hardy/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/12/10/fixing-the-vertical-panel-window-list-on-ubuntu-hardy/">&lt;p&gt;For a long time now I&#x27;ve been bugged by &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugzilla.gnome.org&#x2F;show_bug.cgi?id=86382&quot;&gt;GNOME Bug 86382&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Recent versions of GNOME have featured a window list applet which behaves badly when situated on a vertical panel (which is where I like to have mine). Older versions were ok except the buttons would expand and contract to make enough vertical space to hold the window title if written vertically, even though the window titles were written horizontally. Recent versions have much worse behaviour where once you get over about 7 windows open the window list will flicker rapidly between displaying in 1 or two columns making it almost impossible to click on the button you want.&lt;&#x2F;p&gt;
&lt;p&gt;There have been various patches attached to the bug, and today I managed to get the latest installed on my Ubuntu Hardy 8.04 machine by building custom .debs for the relevant components. I&#x27;m posting exactly what I did here in case it&#x27;s useful to anyone who wants to get this patch installed, and also as a general reference for what you need to do to apply a patch and install it using Ubuntu&#x27;s (or Debian&#x27;s) package management system as a well-behaved package.&lt;&#x2F;p&gt;
&lt;p&gt;The patches are to libwnck and gnome-panel, and they implement the behaviour I want, which is horizontal text, and buttons of fixed height in a window list which expands as needed to fill the available space. With this patch installed, I find a vertical panel is the best way to manage a large number of open windows.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t recommend running this script - better to read and understand it yourself. Nevertheless (and with no guarantees whatsoever) if you do run it as is on a Hardy machine, it should install patched versions of libwnck and gnome-panel which work better with vertical panels.&lt;&#x2F;p&gt;
&lt;pre&gt;# Get all the libraries and source code we need
sudo apt-get install build-essential fakeroot dpkg-dev
sudo apt-get build-dep libwnck gnome-panel

# Create a directory to do all our messing about in
mkdir build-libwnck
cd build-libwnck&#x2F;

# Get the source code for the 2 components we are building
apt-get source libwnck
apt-get source gnome-panel

# Do libwnck first
cd libwnck-2.22.3

# Create a changelog entry
cat &amp;lt;&amp;lt;END &amp;gt; new-entry.txt
libwnck (2.22.3-0ubuntu2) hardy-proposed; urgency=low

  * applied libwnck vertical window list fix from Alexey Rusakov and Vincent Untz

 -- Anon &amp;lt;anon@example.com&amp;gt;  Wed, 10 Dec 2008 20:50:00 +0000

END

cat new-entry.txt debian&#x2F;changelog &amp;gt; debian&#x2F;changelog-new
rm debian&#x2F;changelog
mv debian&#x2F;changelog-new debian&#x2F;changelog
rm new-entry.txt

# Get the libwnck patch and apply it
wget -O libwnck-fix-vertical.patch &quot;http:&#x2F;&#x2F;bugzilla.gnome.org&#x2F;attachment.cgi?id=124112&quot;
patch -p2 &amp;lt; libwnck-fix-vertical.patch

# Build the package
aclocal
automake
dpkg-buildpackage -rfakeroot -b

# Install the libwnck packages
cd ..
sudo dpkg -i libwnck*.deb

# Now do gnome-panel
cd gnome-panel-2.22.2

# Create a changelog entry
cat &amp;lt;&amp;lt;END &amp;gt; new-entry.txt
gnome-panel (1:2.22.2-0ubuntu1.2) hardy-proposed; urgency=low

 * applied libwnck vertical window list fix from Alexey Rusakov and Vincent Untz

 -- Anon &amp;lt;anon@example.com&amp;gt; Wed, 10 Dec 2008 20:50:00 +0000

END

cat new-entry.txt debian&#x2F;changelog &amp;gt; debian&#x2F;changelog-new
rm debian&#x2F;changelog
mv debian&#x2F;changelog-new debian&#x2F;changelog
rm new-entry.txt

# Get the gnome-panel patch and apply it
wget -O gnome-panel-fix-vertical.patch &quot;http:&#x2F;&#x2F;bugzilla.gnome.org&#x2F;attachment.cgi?id=107133&quot;
patch -p2 &amp;lt; gnome-panel-fix-vertical.patch

# Build the package
aclocal
automake
dpkg-buildpackage -rfakeroot -b

# Install the gnome-panel packages
cd ..
sudo dpkg -i *panel*.deb
&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Update: fixed incorrect comment - thanks Stefan.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: changed -p0 to -p2 in the patch commands.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alexander Kojevnikov
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You are my hero Andy, thanks!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Save your praise for Alexey Rusakov and Vincent Untz!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;To use my version of the patch which makes the taskbar buttons taller (but in a very bad way for which I should be punished), replace the first wget command with this one:&lt;&#x2F;p&gt;
&lt;p&gt;wget -O libwnck-fix-vertical.patch &quot;&lt;a href=&quot;http:&#x2F;&#x2F;bugzilla.gnome.org&#x2F;attachment.cgi?id=124396&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;bugzilla.gnome.org&#x2F;attachment.cgi?id=124396&quot;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stefan Huszics
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks a bunch Andy. This will allow me to upgrade my laptop to 8.04.&lt;&#x2F;p&gt;
&lt;p&gt;The bonus of learning how to apply patches to .deb is appreciated as well. :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stefan Huszics
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;BTW, you have an error above &quot;Install the gnome-panel packages&quot;&lt;&#x2F;p&gt;
&lt;p&gt;cd ..&lt;br&#x2F;&gt;
sudo dpkg -i libwnck*.deb&lt;&#x2F;p&gt;
&lt;p&gt;obviously &quot;gnome-panel&quot; in the comment is wrong&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stefan Huszics
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Also, I needed to install automake 1.9 for gnome-panel (default on system was 1.7.9, and latest 1.10.1)&lt;br&#x2F;&gt;
and then run&lt;br&#x2F;&gt;
aclocal-1.9&lt;br&#x2F;&gt;
automake-1.9&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks for the info Stephan – I have made the correction.  The biggest bonus for me too was learning how build custom debs.  It lowers the barrier a lot to hacking on GNOME if I don’t have to build an entire new version from SVN, but can just apply little tweaks to the installed version.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, that comes with risks.  I did break my panel completely at one point and had to do:&lt;&#x2F;p&gt;
&lt;p&gt;links2 &lt;a href=&quot;http:&#x2F;&#x2F;archive.ubuntu.com&#x2F;ubuntu&#x2F;pool&#x2F;main&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;archive.ubuntu.com&#x2F;ubuntu&#x2F;pool&#x2F;main&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;and find the right versions of the packages I was replacing and install them over the top of the ones I had broken.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, the advantage of using debs is that I knew exactly which packages I needed to download and install to get back to a working system.  If I’d been building from tarballs it would have been much harder to work out.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    cb474
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;It turned out I had to install automake, because I was getting a &quot;command not found&quot; error when I tried to run aclocal.  This is on a Debian Testing system.&lt;&#x2F;p&gt;
&lt;p&gt;Then I got to the step where I have to run aclocal the first time and I got these two errors:&lt;&#x2F;p&gt;
&lt;p&gt;configure.in:34: warning: macro &lt;code&gt;AM_PROG_LIBTOOL&amp;#39; not found in library
configure.in:73: warning: macro&lt;&#x2F;code&gt;AM_GLIB_GNU_GETTEXT’ not found in library&lt;&#x2F;p&gt;
&lt;p&gt;The first warning was solved by installing libtool. But I’m still getting the second one about gettext and can’t figure out how to solve that problem. There is a &quot;gettext.m4&quot; file in my &#x2F;usr&#x2F;share&#x2F;aclocal. Any suggestions about how to fix this error and move on to the next step?&lt;&#x2F;p&gt;
&lt;p&gt;(Thanks for the Howto, by the way, I really appreciate it.)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Stefan Huszics
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-11&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&amp;gt; It lowers the barrier a lot to hacking on GNOME&lt;&#x2F;p&gt;
&lt;p&gt;Indeed, used to have Gentoo myself and compiled everything from source, so was quite puzzled of how to do things in Ubuntu without breaking the entire desktop (including with future security updates). Only messing with the exact .debs lowers that risk by a lot.&lt;&#x2F;p&gt;
&lt;p&gt;Now I just need to figure out how to hack the patch so it doesnt rotate the text already at &amp;lt;80px , &amp;lt;30px would fit me a lot better.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Billy Buerger
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’ve been following that bug for a while now.  But didn’t know where to start for applying the patch.  Thanks for the help.  Although I’m running 8.10 which uses V2.24.1 of the gnome packages.  So my first attempt to apply the patch failed and broke things pretty good.  Maybe there’s a better way, but I went through the changes in the patch and made the modifications manually.  Now I finally have my window list working correctly in Ubuntu.  About freakin’ time.&lt;&#x2F;p&gt;
&lt;p&gt;Now if only some of the other applets played better in vertical panels.  At least those are mostly cosmetic.  And since your little article here pointed me in the right direction for installing modifications, maybe I’ll play around with editing some of them myself.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Billy – glad to be of help!  Have fun making the other applets behave.  Which ones cause you problems?  I don’t tend to run many myself.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Billy Buerger
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;Main Menu button is pretty big.&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Quick Lounge (Launchers List) seems to always want to be square.  But at least it displays the icons horizontally first.&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Notification icons sometimes are big.  But it doesn’t seem like they always are.  Also stacks them vertically instead of horizontal.&lt;br&#x2F;&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Many of them stretch the image to the full width of the panel.  Similar to what the main menu seems to do.  Trash and Volume come to mind.  Although neither are particularly important.  Especially since the sound doesn’t work correctly on my laptop.  Also seems that these aren’t always a problem depending on your icon set.  If it doesn’t have a big icon in the theme, then it stays small.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-19&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;@cb474: Sorry, I missed your comment in the spam moderation queue.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t know how to fix that, sorry.  Is gettext itself a separate package that can be installed?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Walter Huf
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I just upgraded my laptop to Jaunty, and your walkthrough sadly does not work on Jaunty. When I try to run the first dpkg-buildpackage, it compiles and then fails after installing a bunch of html files. The exact error message is:&lt;br&#x2F;&gt;
…….&lt;br&#x2F;&gt;
— Installing .&#x2F;html&#x2F;WnckTasklist.html&lt;br&#x2F;&gt;
— Installing .&#x2F;html&#x2F;WnckWindow.html&lt;br&#x2F;&gt;
— Installing .&#x2F;html&#x2F;WnckWorkspace.html&lt;br&#x2F;&gt;
&#x2F;bin&#x2F;bash: line 20: —relative: command not found&lt;br&#x2F;&gt;
make[3]: *** [install-data-local] Error 127&lt;br&#x2F;&gt;
make[3]: Leaving directory &lt;code&gt;&#x2F;home&#x2F;hufman&#x2F;wnk.new&#x2F;libwnck-2.26.0&#x2F;doc&amp;#39;
make[2]: *** [install-am] Error 2
make[2]: Leaving directory&lt;&#x2F;code&gt;&#x2F;home&#x2F;hufman&#x2F;wnk.new&#x2F;libwnck-2.26.0&#x2F;doc’&lt;br&#x2F;&gt;
make[1]: *** [install-recursive] Error 1&lt;br&#x2F;&gt;
make[1]: Leaving directory `&#x2F;home&#x2F;hufman&#x2F;wnk.new&#x2F;libwnck-2.26.0’&lt;br&#x2F;&gt;
make: *** [common-install-impl] Error 2&lt;br&#x2F;&gt;
dpkg-buildpackage: failure: fakeroot debian&#x2F;rules binary gave error exit status 2&lt;&#x2F;p&gt;
&lt;p&gt;Do you have any suggestions? Everything worked fine in Intrepid… I don’t know what’s wrong.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;@Walter, I’m sorry I don’t have any idea what’s causing this :(  Is it just the docs that are going wrong?  If so, there might be a .&#x2F;configure flag that disables building the documentation?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    John Bartholomew
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;First, thank you so much for writing this, doing it this way was much easier than a manual build from the gnome git repositories (which I tried first).&lt;&#x2F;p&gt;
&lt;p&gt;@Walter: I had exactly the same problem. The reason (well, immediate reason at least) is that the GTKDOC_REBASE variable isn’t being set up, and make is trying to run ‘$(GTKDOC_REBASE) —relative …’, which expands into just ‘—relative …’, which bash then complains about since there is no —relative command.  I don’t know what the real underlying cause is though.  My solution was: make sure the gtkdoc package is installed, check that &#x2F;usr&#x2F;bin&#x2F;gtkdoc-rebase exists, and then run ‘export GTKDOC_REBASE=&#x2F;usr&#x2F;bin&#x2F;gtkdoc-rebase’ before re-running dpkg-buildpackage. The build should now succeed. I didn’t have any other problems with Andy’s instructions, on a fresh Jaunty install.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    John Bartholomew
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;nb: Just for completeness (not that it really matters), all these ‘—relative’ things are &lt;em&gt;two&lt;&#x2F;em&gt; dashes, then ‘relative’ — wordpress is just turning double-dash into an em-dash (which is quite right, just not good for code :P)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;@John, thanks for the encouragement, and thanks for the advice for Walter!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    amnuxoll
    &lt;span class=&quot;comment_date&quot;&gt;2009-05-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;cb474:&lt;&#x2F;p&gt;
&lt;p&gt;I also got the AM_GLIB_GNU_GETTEXT error.  I just fixed it by installing the libglib2.0-dev package.&lt;&#x2F;p&gt;
&lt;p&gt;Now let’s see if the rest of this works…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    amnuxoll
    &lt;span class=&quot;comment_date&quot;&gt;2009-05-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;No go for me.  I got this error installing the patched gnome-panel:&lt;&#x2F;p&gt;
&lt;p&gt;dpkg: dependency problems prevent configuration of gnome-panel:&lt;br&#x2F;&gt;
gnome-session (2.26.0svn20090408-0ubuntu2) breaks gnome-panel (&amp;lt;&amp;lt; 1:2.23) and is installed.&lt;&#x2F;p&gt;
&lt;p&gt;Apparently I need to update gnome-session too?  I can tell I’m in up to my neck already and don’t want to tinker too much for fear of trashing the whole thing.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    amnuxoll
    &lt;span class=&quot;comment_date&quot;&gt;2009-05-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Ok, it &lt;em&gt;does&lt;&#x2F;em&gt; work.  I just needed to ignore the scary errors and have faith.  Upon reboot the panel bug is gone on my system.  Thank you, Andy!&lt;&#x2F;p&gt;
&lt;p&gt;The Synaptic Package Manager complains that I’ve got a broken dependency now but I’m pretty sure I’ll just have to live with that.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alexander Kojevnikov
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Carey Underwood posted a patch in bgo#86382 which works quite well. Instructions for Arch Linux are here: &lt;a href=&quot;http:&#x2F;&#x2F;versia.com&#x2F;2009&#x2F;09&#x2F;05&#x2F;arch-linux-yaourt-customizepkg-beauty&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;versia.com&#x2F;2009&#x2F;09&#x2F;05&#x2F;arch-linux-yaour...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Alexander – how does this patch improve on the older ones?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Alexander Kojevnikov
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Both do the job, but Carey’s patch is much simpler and smaller; and changes only libwnck&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Dylan
    &lt;span class=&quot;comment_date&quot;&gt;2010-11-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;does anyone have an updated patch for the latest gnome-panels? I can’t get it working…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam’s Blog » Blog Archive » Reclaiming desktop space with Ubuntu’s window-picker-applet
    &lt;span class=&quot;comment_date&quot;&gt;2011-06-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] a vertical task bar on the left and launcher buttons across the top. I’ve struggled with a long-standing bug with using a vertical panel, and I’ve sacrificed quite a lot of screen space to make enough room to see the window […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>An actual difficult bug fixed</title>
          <pubDate>Wed, 08 Oct 2008 03:21:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/10/08/an-actual-difficult-bug-fixed/</link>
          <guid>https://artificialworlds.net/blog/2008/10/08/an-actual-difficult-bug-fixed/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/10/08/an-actual-difficult-bug-fixed/">&lt;p&gt;Of course, I am bound to get a bug report immediately I have posted this telling me my fix breaks everything, but for the moment I am chuffed that I found, tested, and fixed a genuinely &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.launchpad.net&#x2F;freeguide-tv&#x2F;+bug&#x2F;270348&quot;&gt;difficult bug&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I am particularly proud because I wrote an automated test to ensure it can never happen again, and I used that test to make the debugging process much easier than it otherwise would be. The code that reads, processes and stores listings in FreeGuide is a spider&#x27;s web of interfaces and helper classes (because of the arguably over-engineered plugins framework used for all the moving parts), and tracking this down with plain old-fashioned debugging would have been a huge job.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I bet you are dying to hear what the bug was, aren&#x27;t you?&lt;&#x2F;p&gt;
&lt;p&gt;When you have a programme already stored in FreeGuide, and then a new one comes along that overlaps it, the old programme is deleted. For example if we start off with:&lt;&#x2F;p&gt;
&lt;pre&gt;... 19:00 Top Gear ................... 20:00 Charlie and Lola .............&lt;&#x2F;pre&gt;
&lt;p&gt;but then later download listings again and get these programmes:&lt;&#x2F;p&gt;
&lt;pre&gt;... 19:00 Pocoyo .. 19:15 Round the World ...... &lt;&#x2F;pre&gt;
&lt;p&gt;Then &lt;em&gt;Top Gear&lt;&#x2F;em&gt; will disappear, and be replaced by the 2 new programmes. In fact, any old programme that overlaps any new incoming programme will be automatically deleted.&lt;&#x2F;p&gt;
&lt;p&gt;At least, that is what is supposed to happen. In fact, the real situation is a little more complex because the programmes are stored in separate files (.ser files) for different days and times. Actually, there are 4 files for each day, named things like &quot;day-2008-09-15-A.ser&quot;, where the suffix A, B, C and D indicate which part of each day a file is for.&lt;&#x2F;p&gt;
&lt;p&gt;So imagine what happens when the first set of programmes comes in looking like this:&lt;&#x2F;p&gt;
&lt;pre&gt;19:00 Programme 1A ......... 21:15 Programme 1B .. 21:30 Programme 1C ........... 22:00&lt;&#x2F;pre&gt;
&lt;p&gt;and then the second comes in like this:&lt;&#x2F;p&gt;
&lt;pre&gt;19:00 Programme 2A......................................... 21:45 Programme 2C .. 22:00&lt;&#x2F;pre&gt;
&lt;p&gt;So obviously the old 3 programmes should be completey deleted, and the new 2 should be what you see.&lt;&#x2F;p&gt;
&lt;p&gt;But you don&#x27;t. In fact what you see is programme 1B and programme 2C, before 1B and between the two. Weird huh?&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Why?&quot; I hear you ask. Well, it&#x27;s simple when you consider how the programmes are split into files.&lt;&#x2F;p&gt;
&lt;p&gt;Programme 1A goes into file day-2008-09-14-D.ser, and programmes 1B and 1C go into day-2008-09-15-A.ser.&lt;&#x2F;p&gt;
&lt;p&gt;[Side note: this is true in this case because the bug reporter is in the GMT -0400 timezone and the file boundaries are quarters of a day in GMT.]&lt;&#x2F;p&gt;
&lt;p&gt;Then, when the new programmes come along, 2A goes into 14-D - wiping out 1A, and 2C goes into 15-A - wiping out 1C but not 1B.&lt;&#x2F;p&gt;
&lt;p&gt;Then, when the files get read back in again later, 2A is read from 14-D, but then 1B is read from 15-A, wiping out 2A, and finally 2C is read in as well from 15-A, so we end up with 1B and 2C.&lt;&#x2F;p&gt;
&lt;p&gt;How to fix it? Well, what I did was leave everything as it is, and then do the final read in the reverse order. This means we read in 1B and 2C, but then we read 2A later, and it wipes out 1B, leaving 2A and 2C as we would expect.&lt;&#x2F;p&gt;
&lt;p&gt;Neat fix eh? It works because this kind of wrongness in the .ser files will only exist when a programme hanging off the end should have wiped out something in a later file. Because programmes are classified into files by their start time, they can only hang off the end of a file, not the beginning, so reading the files in backwards will always read the hanging-over file last, wiping out anything which should have been wiped out earlier.&lt;&#x2F;p&gt;
&lt;p&gt;There is a little bug&#x2F;feature remaining, but it only applies when you get some really weird listings from your provider. If you had a programme like 1A (19:00 - 21:15), and downloaded new listings, which ONLY contained a programme overlapping it, but falling into a later file (so maybe it starts at 21:00), and didn&#x27;t contain any programme starting at 19:00, then the backwards reading would mean you would never see your new programme because it would be wiped out by 1A.&lt;&#x2F;p&gt;
&lt;p&gt;This is a very unusual case though, since normally if you get a new programme at 21:00, you will also get new programmes leading up to it, if only to reflect the fact that 1A is now a different length. So this is really a theoretical bug, which explains why I&#x27;ve decided not to fix it...&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, by the time I&#x27;d fiddled with my test for this to get the bug to trigger (which took a long time - working out which bits to fake out and which to test at all was tricky), the actual fix was easily implemented (1 line of code I chose to break out into 3), and then validated in a single click.&lt;&#x2F;p&gt;
&lt;p&gt;Just in case I hadn&#x27;t mentioned it, I love tests.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10.9</title>
          <pubDate>Tue, 09 Sep 2008 22:33:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/09/09/freeguide-0109/</link>
          <guid>https://artificialworlds.net/blog/2008/09/09/freeguide-0109/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/09/09/freeguide-0109/">&lt;p&gt;So let&#x27;s quietly forget FreeGuide 0.10.8, which basically didn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;I fixed the problem (which was some nasty threading thing) by making the initial &quot;Choose Channels for XMLTV&quot; call from the first time wizard not do the clever stuff of displaying errors to the user. Instead it dumps any errors to the console (which is an improvement over swallowing them completely as it did before), and when you run &quot;Choose Channels for XMLTV&quot; from the menu, or &quot;Configure&quot; from the Options screen, you still get the errors displayed in a nice dialog as in 0.10.8.&lt;&#x2F;p&gt;
&lt;p&gt;More excitingly, new in this release: some unit tests!&lt;&#x2F;p&gt;
&lt;p&gt;You can run them by doing this in the src directory:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;java freeguide.test.slow.FreeGuideSlowTest&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;They test the fixes I made in 0.10.9 for XMLTV files that have certain attributes missing, and have different date formats.&lt;&#x2F;p&gt;
&lt;p&gt;The date formats were supposed to be already working, but a fix I made several years (yes, &lt;em&gt;years&lt;&#x2F;em&gt;) ago broke them. Why didn&#x27;t I catch the problem? No tests.&lt;&#x2F;p&gt;
&lt;p&gt;The second age of FreeGuide is coming. I shall call it The Time of Testing.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Wii repaired free!</title>
          <pubDate>Tue, 09 Sep 2008 22:19:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/09/09/wii-repaired-free/</link>
          <guid>https://artificialworlds.net/blog/2008/09/09/wii-repaired-free/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/09/09/wii-repaired-free/">&lt;p&gt;A few weeks ago my Nintendo Wii started not accepting any Wii disks (but was still fine for GameCube ones... interesting). It made a clicking noise for a bit and then declared &quot;Bad Disk&quot; or something like that.&lt;&#x2F;p&gt;
&lt;p&gt;I need my Wii. I watch all my TV through it, using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;recordtv.sourceforge.net&#x2F;&quot;&gt;recordtv&lt;&#x2F;a&gt;. I had to get it fixed.&lt;&#x2F;p&gt;
&lt;p&gt;I looked up the official support options, and the deal in the UK is that you go to http:&#x2F;&#x2F;www.nintendoservicecentre.co.uk and they send you an envelope. You put your Wii in the envelope (postage paid) and they have a look at it. Then they give you one of two fixed prices: either Â£28 or Â£70 depending on the severity of the problem. (I guess Â£70 is about the cost price of a Wii?). If you agree on the price, you pay them and they send it back fixed, postage paid.&lt;&#x2F;p&gt;
&lt;p&gt;So I sent mine off, worried and entertainment-less. I heard nothing for about a week, and then it returned, fixed, with a little sticker on it saying &quot;Repair: Wii minor, Â£0.00.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Paint me satisfied.&lt;&#x2F;p&gt;
&lt;p&gt;+1 Nintendo.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Another one</title>
          <pubDate>Mon, 01 Sep 2008 21:08:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/09/01/another-one/</link>
          <guid>https://artificialworlds.net/blog/2008/09/01/another-one/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/09/01/another-one/">&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2008&#x2F;09&#x2F;baby3-12weeks-annotated-small.jpg&quot; alt=&quot;3rd Baby scan, 12 weeks&quot; title=&quot;baby3-12weeks-annotated-small&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Due in March. Very excited.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10.8</title>
          <pubDate>Mon, 28 Jul 2008 20:00:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/07/28/freeguide-0108/</link>
          <guid>https://artificialworlds.net/blog/2008/07/28/freeguide-0108/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/07/28/freeguide-0108/">&lt;p&gt;I am still working slowly on moving FreeGuide forward. Somehow it seems my itches for FreeGuide are all about making it less annoying for people who are trying it the first time. I guess this is motivated by my desire for world domination.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, we are one small step closer to my mum being able to use FreeGuide - when the &quot;Choose channels&quot; step (i.e. the XMLTV grabber configuration) goes wrong, you can now see a real genuine error message, and hopefully figure out what went wrong.&lt;&#x2F;p&gt;
&lt;p&gt;Actually, it always used to work that way but the error-catching got refactored away at some point. Anyway, I am slowly taking the ground back...&lt;&#x2F;p&gt;
&lt;p&gt;As I do more and more test-driven development at work I am becoming completely addicted. For this FreeGuide code I wrote a couple of unit tests but they are not within a proper framework, and can&#x27;t be launched easily as a test suite. I am considering JUnit.&lt;&#x2F;p&gt;
&lt;p&gt;I also want to set up some component-level tests e.g. for downloading listings for each country and checking everything works as expected. It&#x27;s brilliant fun having tests in place, but when you have as little time as I have for FreeGuide at the moment, it&#x27;s difficult to decide to spend a long time working on a test framework when I could be fixing a &quot;real user problem&quot; or adding a cool new feature.&lt;&#x2F;p&gt;
&lt;p&gt;But I&#x27;ve got the testing bug badly, so watch this space.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Debugging memory use and fragmentation on Windows using Address Space Monitor</title>
          <pubDate>Fri, 18 Apr 2008 10:29:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/04/18/debugging-memory-use-and-fragmentation-on-windows/</link>
          <guid>https://artificialworlds.net/blog/2008/04/18/debugging-memory-use-and-fragmentation-on-windows/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/04/18/debugging-memory-use-and-fragmentation-on-windows/">&lt;p&gt;At work at the moment we are putting a lot of effort into making our program not crash. Sensible, eh?&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s crashing because a) it uses an enormous amount of memory and b) it tends to fragment your remaining memory. One of the characteristics of this program is that from time to time it needs a large contiguous chunk of memory so that it can pass a big bit of XML to someone else. This tends to be a problem when your memory is fragmented.&lt;&#x2F;p&gt;
&lt;p&gt;For example today I was running it, and it was using 1GB of memory (+about 0.3GB reserved), leaving 0.7GB free in the 2GB address space. However, the largest available contiguous block of memory was about 60MB.&lt;&#x2F;p&gt;
&lt;p&gt;The long-term answer is obviously to use less memory, but in the shorter term we have had quite a bit of success by writing code that works around the fragmentation. For example, we have a custom mutable string-like class that can store its chars in several separate blocks of memory instead of all in one contiguous block.&lt;&#x2F;p&gt;
&lt;p&gt;Debugging this kind of thing is always a very difficult process. For me it has transformed from an almost-impossible task into a tractible one because of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ccgi.hashpling.plus.com&#x2F;blog&#x2F;&quot;&gt;Charles Bailey&lt;&#x2F;a&gt;&#x27;s &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;hashpling.org&#x2F;asm&#x2F;&quot;&gt;Address Space Monitor&lt;&#x2F;a&gt; tool. This gives you a clear picture of your process&#x27; address space, and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ccgi.hashpling.plus.com&#x2F;blog&#x2F;address-space-monitor-06-release&#x2F;&quot;&gt;version 0.6a&lt;&#x2F;a&gt; adds recording functionality that makes reports to impress managers a lot easier to produce.&lt;&#x2F;p&gt;
&lt;p&gt;Without it, we wouldn&#x27;t know where to start.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>C++ is an expert language</title>
          <pubDate>Mon, 07 Apr 2008 09:26:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/04/07/c-is-an-expert-language/</link>
          <guid>https://artificialworlds.net/blog/2008/04/07/c-is-an-expert-language/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/04/07/c-is-an-expert-language/">&lt;p&gt;&lt;strong&gt;Update: I should point out at the beginning that I love C++. Anything below which sounds bitter or critical is borne of a deep and growing love. C++ is a journey into worlds of beauty and strength.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I assert that C++ is an expert language. What do I mean?&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You shouldn&#x27;t be allowed to use C++ in anger unless you&#x27;ve used it for 2 years in anger.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;em&gt;Aside: what should we do about this?&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;In practice this means that no-one should recruit newbie C++ developers.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;The only alternative is to have some kind of apprenticeship system, where all the code written by a newbie is re-written by their mentor for about 2 years. This is a great learning experience, and could weed out people with insufficient capacity for humility to be a C++ programmer. (Note I say capacity for humility, because the actual humility will be provided by the constant crushing of your spirit provided by someone tearing your code apart every day.)&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In Java, to create an &quot;array&quot;, and add an object to it, you do this:&lt;&#x2F;p&gt;
&lt;pre&gt;MyObj obj = new MyObj();
ArrayList&amp;lt;MyObj&amp;gt; arr = new ArrayList&amp;lt;MyObj&amp;gt;();
arr.add( obj );
&lt;&#x2F;pre&gt;
&lt;p&gt;In Python, you do this:&lt;&#x2F;p&gt;
&lt;pre&gt;obj = MyObj()
arr = []
arr.append( obj )
&lt;&#x2F;pre&gt;
&lt;p&gt;In C, you do this:&lt;&#x2F;p&gt;
&lt;pre&gt;MyStruct obj;
MyStruct arr[50] = { obj };
&lt;&#x2F;pre&gt;
&lt;p&gt;In Perl, you do this:&lt;&#x2F;p&gt;
&lt;pre&gt;my $obj = MyObj-&amp;gt;new();
my @arr;
push(@arr,$obj);
&lt;&#x2F;pre&gt;
&lt;p&gt;In Pascal, you do this:&lt;&#x2F;p&gt;
&lt;pre&gt;var
    arr : ARRAY [1..50] of int;
begin
    arr[1] := 7;
end
&lt;&#x2F;pre&gt;
&lt;p&gt;In Haskell, you might do something like this (thanks to Neil Mitchell):&lt;&#x2F;p&gt;
&lt;pre&gt;[myObj]
&lt;&#x2F;pre&gt;
&lt;p&gt;Don&#x27;t get het up about the fact that these examples do different things: my point is, they are reasonable ways of performing the task (add something to an array) in the languages chosen. (Please do send in corrections, though - none of these were checked and they are probably wrong.)&lt;&#x2F;p&gt;
&lt;p&gt;Note that the C example hides a little more complexity because you need to make sure you tidy up your memory afterwards.&lt;&#x2F;p&gt;
&lt;p&gt;Now, what do you do in C++? It&#x27;s just as easy, right?&lt;&#x2F;p&gt;
&lt;pre&gt;MyObj obj;
std::vector&amp;lt;MyObj&amp;gt; arr;
arr.push_back( obj );
&lt;&#x2F;pre&gt;
&lt;p&gt;WRONG!&lt;&#x2F;p&gt;
&lt;p&gt;In the examples above, &lt;strong&gt;you don&#x27;t need to know what is going on under the covers&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, in general I suggest there are broadly two types of programming language around at the moment: those where you have to know how things work, but where how things work is quite simple (e.g. C, assembly languages, maybe FORTRAN and COBOL) and those which isolate you from how things work (all the rest?).&lt;&#x2F;p&gt;
&lt;p&gt;Where does C++ fit in to this scheme? It is in the unique position of being a language which has incredibly complex things going on under the covers, and &lt;strong&gt;you have to know about it!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What do I mean by saying you have to know what&#x27;s going on under the covers?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s look at our example again, and ask this question: what types of object can you put in the array? In the other programming languages above, you can essentially put any &quot;normal&quot; objects (where normal is different for each example) into the array.&lt;&#x2F;p&gt;
&lt;p&gt;In C++, here are some of the rules you need to understand about objects you can put into std::vector. You should understand these before you try using std::vector. If you can&#x27;t understand them, you should think hard until you do.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;default-constructor&quot;&gt;Default constructor&lt;&#x2F;h3&gt;
&lt;p&gt;If you want to give the size of the vector when you create it (or resize it later), your object must have a default constructor [&lt;em&gt;Stroustrup §16.3.4&lt;&#x2F;em&gt;].&lt;&#x2F;p&gt;
&lt;p&gt;(Note: if you don&#x27;t define any other constructors, the compiler will automatically define a default constructor for you (which may or may not do what you want). If you do, the default constructor is the one that can be called without any arguments [&lt;em&gt;Stroustrup §10.4.2&lt;&#x2F;em&gt;].&lt;&#x2F;p&gt;
&lt;p&gt;Example:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat default_constructor_required.cpp
#include &amp;lt;vector&amp;gt;

class MyObject
{
public:
        MyObject( int num )
        {
        }
};

int main( int argc, const char* argv[] )
{
        std::vector&amp;lt;MyObject&amp;gt; arr( 5 );
}

$ g++ default_constructor_required.cpp
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:
In constructor â€˜std::vector&amp;lt;_Tp, _Alloc&amp;gt;::vector(size_t) [with
_Tp = MyObject, _Alloc = std::allocator&amp;lt;MyObject&amp;gt;]â€™:
default_constructor_required.cpp:12:   instantiated from here
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:219:
error: no matching function for call to â€˜MyObject::MyObject()â€™
default_constructor_required.cpp:5: note: candidates are: MyObject::MyObject(int)
default_constructor_required.cpp:4: note:                 MyObject::MyObject(const MyObject&amp;amp;)
&lt;&#x2F;pre&gt;
&lt;p&gt;Nice error message, eh?&lt;&#x2F;p&gt;
&lt;p&gt;(Note: if your array contains a built-in type (e.g. int) it will be initialised to 0 in its default constructor [&lt;em&gt;Stroustrup §4.9.5&lt;&#x2F;em&gt;].&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t want to give the size of the vector when you create it (not recommended), then you don&#x27;t need a default constructor in your object [&lt;em&gt;Stroustrup §16.3.4&lt;&#x2F;em&gt;].&lt;&#x2F;p&gt;
&lt;h3 id=&quot;copy-constructor&quot;&gt;Copy constructor&lt;&#x2F;h3&gt;
&lt;p&gt;Your object must also have a copy constructor. Example:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat copy_constructor_required.cpp
#include &amp;lt;vector&amp;gt;

class MyObject
{
public:
        MyObject()
        {
        }
private:
        MyObject( const MyObject&amp;amp; );
};

int main( int argc, const char* argv[] )
{
        std::vector&amp;lt;MyObject&amp;gt; arr( 5 );
}

$ g++ copy_constructor_required.cpp
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h: In
constructor â€˜std::vector&amp;lt;_Tp, _Alloc&amp;gt;::vector(size_t) [with _Tp = MyObject,
_Alloc = std::allocator&amp;lt;MyObject&amp;gt;]â€™:
copy_constructor_required.cpp:15:   instantiated from here
copy_constructor_required.cpp:10: error: â€˜MyObject::MyObject(const MyObject&amp;amp;)â€™ is private
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:219:
error: within this context
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_construct.h:
In function â€˜void std::_Construct(_T1*, const _T2&amp;amp;) [with _T1 = MyObject, _T2 = MyObject]â€™:
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_uninitialized.h:194:
   instantiated from â€˜void std::__uninitialized_fill_n_aux(_ForwardIterator, _Size,
 const _Tp&amp;amp;, __false_type) [with _ForwardIterator = MyObject*, _Size = unsigned int, _Tp = MyObject]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_uninitialized.h:218:
   instantiated from â€˜void std::uninitialized_fill_n(_ForwardIterator, _Size,
 const _Tp&amp;amp;) [with _ForwardIterator = MyObject*, _Size = unsigned int, _Tp = MyObject]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_uninitialized.h:310:
   instantiated from â€˜void std::__uninitialized_fill_n_a(_ForwardIterator, _Size,
 const _Tp&amp;amp;, std::allocator&amp;lt;_Tp2&amp;gt;) [with _ForwardIterator = MyObject*, _Size
 = unsigned int, _Tp = MyObject, _Tp2 = MyObject]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:219:
   instantiated from â€˜std::vector&amp;lt;_Tp, _Alloc&amp;gt;::vector(size_t) [with _Tp =
 MyObject, _Alloc = std::allocator&amp;lt;MyObject&amp;gt;]â€™
copy_constructor_required.cpp:15:   instantiated from here
copy_constructor_required.cpp:10: error: â€˜MyObject::MyObject(const
 MyObject&amp;amp;)â€™ is private
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_construct.h:81:
 error: within this context
&lt;&#x2F;pre&gt;
&lt;p&gt;(Aside: this program is 181 bytes, and the error message is 1950 bytes.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;assignment-operator&quot;&gt;Assignment operator&lt;&#x2F;h3&gt;
&lt;p&gt;You also need operator=. Example:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat assignment_operator_required.cpp
#include &amp;lt;vector&amp;gt;

class MyObject
{
private:
        MyObject&amp;amp; operator=( const MyObject&amp;amp; );
};

int main( int argc, const char* argv[] )
{
        MyObject obj;
        std::vector&amp;lt;MyObject&amp;gt; arr;
        arr.push_back( obj );
}

$ g++ assignment_operator_required.cpp
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;vector.tcc:
In member function â€˜void std::vector&amp;lt;_Tp,
_Alloc&amp;gt;::_M_insert_aux(__gnu_cxx::__normal_iterator&amp;lt;typename _Alloc::pointer,
std::vector&amp;lt;_Tp, _Alloc&amp;gt; &amp;gt;, const _Tp&amp;amp;) [with _Tp = MyObject, _Alloc =
std::allocator&amp;lt;MyObject&amp;gt;]â€™:
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:610:
instantiated from â€˜void std::vector&amp;lt;_Tp, _Alloc&amp;gt;::push_back(const _Tp&amp;amp;) [with
_Tp = MyObject, _Alloc = std::allocator&amp;lt;MyObject&amp;gt;]â€™
assignment_operator_required.cpp:13: instantiated from here
assignment_operator_required.cpp:6: error: â€˜MyObject&amp;amp;
MyObject::operator=(const MyObject&amp;amp;)â€™ is private
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;vector.tcc:260:
error: within this context
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_algobase.h:
In static member function â€˜static _BI2 std::__copy_backward&amp;lt;_BoolType,
std::random_access_iterator_tag&amp;gt;::copy_b(_BI1, _BI1, _BI2) [with _BI1 =
MyObject*, _BI2 = MyObject*, bool _BoolType = false]â€™:
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_algobase.h:443:
instantiated from â€˜_BI2 std::__copy_backward_aux(_BI1, _BI1, _BI2) [with _BI1
= MyObject*, _BI2 = MyObject*]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_algobase.h:482:
instantiated from â€˜static _BI2 std::__copy_backward_normal&amp;lt;true,
true&amp;gt;::copy_b_n(_BI1, _BI1, _BI2) [with _BI1 =
__gnu_cxx::__normal_iterator&amp;lt;MyObject*, std::vector&amp;lt;MyObject,
std::allocator&amp;lt;MyObject&amp;gt; &amp;gt; &amp;gt;, _BI2 = __gnu_cxx::__normal_iterator&amp;lt;MyObject*,
std::vector&amp;lt;MyObject, std::allocator&amp;lt;MyObject&amp;gt; &amp;gt; &amp;gt;]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_algobase.h:517:
instantiated from â€˜_BI2 std::copy_backward(_BI1, _BI1, _BI2) [with _BI1 =
__gnu_cxx::__normal_iterator&amp;lt;MyObject*, std::vector&amp;lt;MyObject,
std::allocator&amp;lt;MyObject&amp;gt; &amp;gt; &amp;gt;, _BI2 = __gnu_cxx::__normal_iterator&amp;lt;MyObject*,
std::vector&amp;lt;MyObject, std::allocator&amp;lt;MyObject&amp;gt; &amp;gt; &amp;gt;]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;vector.tcc:257:
instantiated from â€˜void std::vector&amp;lt;_Tp,
_Alloc&amp;gt;::_M_insert_aux(__gnu_cxx::__normal_iterator&amp;lt;typename _Alloc::pointer,
std::vector&amp;lt;_Tp, _Alloc&amp;gt; &amp;gt;, const _Tp&amp;amp;) [with _Tp = MyObject, _Alloc =
std::allocator&amp;lt;MyObject&amp;gt;]â€™
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_vector.h:610:
instantiated from â€˜void std::vector&amp;lt;_Tp, _Alloc&amp;gt;::push_back(const _Tp&amp;amp;) [with
_Tp = MyObject, _Alloc = std::allocator&amp;lt;MyObject&amp;gt;]â€™
assignment_operator_required.cpp:13: instantiated from here
assignment_operator_required.cpp:6: error: â€˜MyObject&amp;amp;
MyObject::operator=(const MyObject&amp;amp;)â€™ is private
&#x2F;usr&#x2F;lib&#x2F;gcc&#x2F;i486-linux-gnu&#x2F;4.0.3&#x2F;..&#x2F;..&#x2F;..&#x2F;..&#x2F;include&#x2F;c++&#x2F;4.0.3&#x2F;bits&#x2F;stl_algobase.h:412:
error: within this context
&lt;&#x2F;pre&gt;
&lt;p&gt;(Aside: program is 202 bytes, error is 2837 bytes.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;excuses&quot;&gt;Excuses&lt;&#x2F;h3&gt;
&lt;p&gt;Don&#x27;t give me that &quot;the compiler will provide them for you&quot; excuse. What the compiler provides is often wrong, unless you&#x27;ve been careful to ensure you don&#x27;t own any members by holding pointers to them: i.e. if you&#x27;ve fully understood the problem I am setting out and avoided the pitfalls.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;I assert that C++ is an expert language. Quite apart from the fact that the method names on STL objects use archane phrases like &quot;push_back&quot; rather than &quot;add&quot;, and the error messages you get from popular compilers are huge and almost incomprehensible, my main point is that you have to understand the basics of how the standard library is implemented, before you can use it. This is expert behaviour.&lt;&#x2F;p&gt;
&lt;p&gt;I have illustrated this point by showing what you need to know to use the standard resizeable array type in C++. You need to know a lot.&lt;&#x2F;p&gt;
&lt;p&gt;More on whether the fact that C++ is an expert language is a bad thing, later.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: simplified the C example thanks to Edmund&#x27;s suggestion.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 2: corrected the Java example thanks to Anon&#x27;s comment.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update 3: corrected the Haskell example thanks to Neil Mitchell&#x27;s comment.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Perl Coding School » Blog Archive » perl code [2008-04-07 11:39:45]
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…]  C++ is an expert language  By Andy Balaam  The only alternative is to have some kind of apprenticeship system, where all the code written by a newbie is re-written by their mentor for about 2 years. This is a great learning experience, and could weed out people with insufficient …   Andy Balaam’s Blog – &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&lt;&#x2F;a&gt; […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Tac-Tics
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Your Haskell and Python examples are whack. Those are the only two I checked, but you should probably be more careful if you’re going to make a point about language inexperience =–)&lt;&#x2F;p&gt;
&lt;p&gt;&amp;gt; obj = MyObj()&lt;br&#x2F;&gt;
&amp;gt; arr = []&lt;br&#x2F;&gt;
&amp;gt; arr.append( obj )&lt;&#x2F;p&gt;
&lt;p&gt;No one would do this. This style is too much like Java =-P A real Pythonist would do it simply like this, unless they needed the handle to obj.&lt;&#x2F;p&gt;
&lt;p&gt;arr = [MyObj()]&lt;&#x2F;p&gt;
&lt;p&gt;The Haskell code is more less good.&lt;br&#x2F;&gt;
&amp;gt; [ get_my_obj :: MyObj ]&lt;br&#x2F;&gt;
In what you have, you annotate the type of get_my_obj inside a list. There are many subtle errors here. First, annotations in Haskell are almost always omitted except for top-level functions. The type checker can infer it all. Next, it’s very awkward to put the annotation INSIDE the list (though, it is totally legal). If the type checker did indeed require get_my_obj to be annotated, it would almost always be written as:&lt;&#x2F;p&gt;
&lt;p&gt;[get_my_obj] :: [MyObj]&lt;&#x2F;p&gt;
&lt;p&gt;Showing that the list, as a whole, is a list containing MyObj objects. Lastly, and this one is the biggest issue, get_my_obj is named as if it were a factory for a MyObj object. But things are weird in Haskell, and get_my_obj is a &lt;em&gt;constant&lt;&#x2F;em&gt;. That is, it doesn’t create a new object every time this line gets run. It returns the same one every time. What you want to do is probably: 1) apply some arguments to it; 2) use a constructor and apply arguments to that, not a function; or 3) use a monad for a side-effectful object creation.&lt;&#x2F;p&gt;
&lt;p&gt;1)&lt;br&#x2F;&gt;
[sin 1.0]&lt;&#x2F;p&gt;
&lt;p&gt;2)&lt;br&#x2F;&gt;
[Person &quot;Haskell&quot; &quot;Curry&quot;]&lt;&#x2F;p&gt;
&lt;p&gt;3)&lt;br&#x2F;&gt;
do {&lt;br&#x2F;&gt;
input&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    T4C
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;However, using C++ has the huge advantages that make it all worth it:&lt;&#x2F;p&gt;
&lt;p&gt;[ ] easier to manage&lt;br&#x2F;&gt;
[ ] performs faster&lt;br&#x2F;&gt;
[ ] faster to code in&lt;br&#x2F;&gt;
[ ] easier to debug&lt;br&#x2F;&gt;
[ ] better object oriented logic&lt;br&#x2F;&gt;
[x] has been around for a long time&lt;br&#x2F;&gt;
[x] shouldn’t be used by anyone who hasn’t been coding in it for two years&lt;&#x2F;p&gt;
&lt;p&gt;That sounds sarcastic but &quot;has been around for a long time&quot; is damning — it curses us with all that legacy code.&lt;&#x2F;p&gt;
&lt;p&gt;The frustrating aspect of C++’s &quot;expert&quot;-ness is that it’s a volume of details, not concepts that are required to be a competent C++ coder.  Internalizing &quot;push_back&quot; is not going to contribute to any lifelong coding skill, that’s for sure.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    markus
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;But one problem is that, with languages like Ruby and Python becoming more popular, and C still being the oldschool powerhorse – why is the complexity inside C++ needed?&lt;&#x2F;p&gt;
&lt;p&gt;I mean newbies produce more errors everywhere, and in C++ you shoot at your foot and the whole neighbor village is gone. But why use C++ when one has C available and can combine it with i.e. Ruby anyway?&lt;&#x2F;p&gt;
&lt;p&gt;To me it seems that the language is just too complex, because if you continually surprise humans, than who is at fault – the language, or the human?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    noah
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;perl could be simplifies to&lt;br&#x2F;&gt;
push @arr, MyObj-&amp;gt;new();&lt;&#x2F;p&gt;
&lt;p&gt;The temproary variable isn’t required. Python quite likely would support similar shortening&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    kL
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;in D:&lt;br&#x2F;&gt;
auto obj = new MyObj;&lt;br&#x2F;&gt;
MyObj[] array;&lt;br&#x2F;&gt;
array ~= obj;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Riaan Minne
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I personally like to believe your point that you have to understand how things work under the hood of a language should be applied to any language. Basic logical computer science concepts like arrays and stack and so forth are arguably universal in the world of computer programming, the language of choice does not change it.&lt;&#x2F;p&gt;
&lt;p&gt;I have been coding Java for almost 8 years now, and yes I admit due to its high level nature I am still learning a thing or two now and then. The difference is that I fully understand these experts &quot;concepts&quot;, and fully confident I can implement it in almost any language of my choice since the computational logic does not change (barring language implementation differences, but not even that should be an issue).&lt;&#x2F;p&gt;
&lt;p&gt;I agree with you main point of C++ being a expert language, but I believe an expert should necessarily be distinguished by his&#x2F;hers language of choice.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Riaan Minne
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I personally like your point, but the idea that you have to understand how things work under the hood of a language should be applied to any language. Basic logical computer science concepts like arrays and stacks and so forth are arguably universal in the world of computer programming, the language of choice does not change it.&lt;&#x2F;p&gt;
&lt;p&gt;I have been coding Java for almost 8 years now, and yes I admit due to its high level nature I am still learning a thing or two about JVM inner workings  now and then. The difference is that I fully understand these experts &quot;concepts&quot;, and fully confident I can implement it in almost any language of my choice since the computational logic does not change (barring language implementation differences, but not even that should be an issue).&lt;&#x2F;p&gt;
&lt;p&gt;I agree with you main point of C++ being a expert language, but I believe an expert should necessarily be distinguished by his&#x2F;hers language of choice.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    rrwood
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I think you are right.  I worked hard at becoming a good C++ programmer, and it wasn’t until I had a fairly deep understanding of the language, compiler, and runtime libs that it all started making any sense.&lt;&#x2F;p&gt;
&lt;p&gt;2 years is probably a little long, but 6 months of fairly intense work with the help of someone who already Knows The Way would be a reasonable minimum.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, now that I’ve actually been working with Python, I can’t ever see myself choosing C++ for new work.  Python with C modules for the compute-intensive stuff is WAY more attractive for anything I can see myself doing.&lt;&#x2F;p&gt;
&lt;p&gt;I suspect that there are cases where C++ really makes sense.  Anybody out there have any good examples?  And I’m not talking about cases where the reason for C++ is the existence of a huge body of legacy code that can’t be changed…..&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Err
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You don’t have to know how the STL is implemented, because the requirements on the type traits of vector elements are specified in the standard. It would be ridiculous to require you to know how the STL is implemented, because there are many implementations; their focal point, however, is the standard.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Anon
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’m no Java pro but do you mean ArrayList (&lt;a href=&quot;http:&#x2F;&#x2F;java.sun.com&#x2F;j2se&#x2F;1.4.2&#x2F;docs&#x2F;api&#x2F;java&#x2F;util&#x2F;ArrayList.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;java.sun.com&#x2F;j2se&#x2F;1.4.2&#x2F;docs&#x2F;api&#x2F;java&#x2F;...&lt;&#x2F;a&gt; ) not Array (&lt;a href=&quot;http:&#x2F;&#x2F;java.sun.com&#x2F;j2se&#x2F;1.4.2&#x2F;docs&#x2F;api&#x2F;java&#x2F;lang&#x2F;reflect&#x2F;Array.html&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;java.sun.com&#x2F;j2se&#x2F;1.4.2&#x2F;docs&#x2F;api&#x2F;java&#x2F;...&lt;&#x2F;a&gt; )?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    erm....
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;if people are not going to hire newbs, how are they going to become !newbs ? (i leave to you what the negation of newb is :) )&lt;&#x2F;p&gt;
&lt;p&gt;as a side comment, i know at least one guy that knew more c++ that some &quot;experienced&quot; c++ developers…&lt;&#x2F;p&gt;
&lt;p&gt;everyone was a newb once, right??&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ric
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Like the C example, the PErl can be simplified from this:&lt;&#x2F;p&gt;
&lt;p&gt;my $obj = MyObj-&amp;gt;new();&lt;br&#x2F;&gt;
my @arr;&lt;br&#x2F;&gt;
push(@arr,$obj);&lt;&#x2F;p&gt;
&lt;p&gt;To this:&lt;&#x2F;p&gt;
&lt;p&gt;my $obj = MyObj-&amp;gt;new();&lt;br&#x2F;&gt;
my @arr = ( $obj );&lt;&#x2F;p&gt;
&lt;p&gt;Or even (if you are careful):&lt;&#x2F;p&gt;
&lt;p&gt;my @arr = ( MyObj-&amp;gt;new() );&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Neil Mitchell
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;In Haskell you would do: [myObj] – type signatures are almost never needed as they are usually inferred.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    fname lname
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;for x=1 to 10&lt;br&#x2F;&gt;
print &quot;Hello World!&quot;&lt;br&#x2F;&gt;
next x&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Mike
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Your example cites only references to the Standard Template Library, which is not an integral part of the C++ language itself.  I would agree that the STL is overly complex and does not work in what would seem to be a logical manner, but I don’t know that I would go so far as to claim that &quot;C++ is an expert language.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Edgardo Portal
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&quot;I have illustrated this point by showing what you need to know to use the standard resizeable array type in C++. You need to know a lot.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;The three things you note about C++ are about the first thing most books mention (along with providing a (possibly virtual) destructor), so I’d venture to guess that most people venturing to use the STL would have that under their belts by then.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sgt. Pepper
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Another stupid and pointless C++ flame…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Max
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;To me it sounds more like you are saying C++ is a language for people that have actually used it for longer than a week.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    B.G.
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have to call bollux on this entire post. The truth is, most developers writing C++ code are doing so using MFC, or the Borland equivalent (C++ Builder) – especially in Europe. I’ve worked for 4 different companies (all successful) over the past 10 years. All used C++ as the primary programming language, and all used either MFC or VCL classes as the backbone of all development. None of them used the STL in any way.&lt;&#x2F;p&gt;
&lt;p&gt;For my very first C++ job I learned the STL, (read the books and so on). But this learning was never used, as the ‘simplicity’ of the other established libraries always won out at the higher, decision making level.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, it’s true that for certain jobs you’re better off writing your own string list, or whatever – but for most Enterprise software (Read: money making software), this is simply not EVER necessary!&lt;&#x2F;p&gt;
&lt;p&gt;This post reads as old-school, pretentious, bullcrap!  What’s next – every car should have a Porsche engine? Every medical procedure MUST be carried out in the number 1 US hospital? Every wife should appear in Maxim’s top 100 list before the ring is offered, otherwise they’re too ugly and should be dumped at the alter?&lt;&#x2F;p&gt;
&lt;p&gt;Get a grip! – on reality that is.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Kyle
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I can’t say I agree with this, for one simple reason:&lt;&#x2F;p&gt;
&lt;p&gt;Every class in C++ must have a default constructor, a copy constructor, and an assignment operator.&lt;&#x2F;p&gt;
&lt;p&gt;Granted, the compiler will let you CREATE a class without these, but even a C++ newbie, even somebody who’s had a single class in the language, knows that every class in C++ must have a default constructor, a copy constructor, and an assignment operator.&lt;&#x2F;p&gt;
&lt;p&gt;Once it becomes automatic to create these methods (and they’re usually very simple to create) for every class you write, you don’t really run into these odd problems with the STL.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Apologies for the delay approving all your posts.  I have been drowning under a torrent of spam for the last couple of weeks.  I will reply to each comment personally this evening or tomorrow.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Misc
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Yeah .. You need to know what you’re doing, which is completely unlike every other language on the planet. Or, perhaps, you can just use the C example in the same old way and move on with your life.&lt;&#x2F;p&gt;
&lt;p&gt;I agree with B.G. – get a grip.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Karl
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;There are two senses of the phrase &quot;how it works.&quot; One is how it works on the inside — in software terms, how it is implemented. The other is how it works from the perspective of someone using it — in software terms, the interface.&lt;&#x2F;p&gt;
&lt;p&gt;In all of the languages that you cited, the array feature has an interface with which the programmer must operate. The interface to the C++ std::vector is certainly complicated, and you could argue that it is overcomplicated, but the details you cited as things you have to know about how it works have nothing to do with the implementation of std::vector, they are only parts of the complicated interface of std::vector.&lt;&#x2F;p&gt;
&lt;p&gt;So, while you can argue that C++ is complicated, your example only shows that C++ is quantitatively more complicated. If you want to argue that C++ occupies an entirely separate &quot;complication class,&quot; you will need a different argument. Such an argument is probably possible.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Frank
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I agree that newbee c++ coders can be detrimental in the core of a complex project.  Bad programmers are detrimental, regardless of the language.&lt;&#x2F;p&gt;
&lt;p&gt;The issue raised in this article is less about cpp and more about garbage collection&#x2F;reference counting.&lt;&#x2F;p&gt;
&lt;p&gt;Of the ‘simple’ examples given, most of them use reference types and garbage collection&#x2F;reference counting.&lt;&#x2F;p&gt;
&lt;p&gt;Pascal used array of int, nothing complex there in cpp either&lt;&#x2F;p&gt;
&lt;p&gt;C example uses value type copied by value.  Ownership of members is ‘undefined’, a crash waiting to happen.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the CPP errors result from passing the object by value.  The compiler is warning you that it can’t do it correctly without being told how.  Change to something like this for semantics comparable to the other examples.&lt;&#x2F;p&gt;
&lt;p&gt;typedef smart_ptr myobj_ptr&lt;br&#x2F;&gt;
std::vector arr;&lt;br&#x2F;&gt;
arr.push_back( new MyObj );&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nick G
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You can simplify the python example as well:&lt;br&#x2F;&gt;
arr = [myObj()]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    raincat
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;To C++’s defence here: the program you have written here is semantically very different from the other languages.  In C++ you have a vector of MyObject, in all other languages (besides C) you have an array of pointers to objects.  The pointer variant in C++ would be:&lt;&#x2F;p&gt;
&lt;p&gt;MyObj obj;&lt;br&#x2F;&gt;
std::vector arr;&lt;br&#x2F;&gt;
arr.push_back( &amp;amp;obj );&lt;&#x2F;p&gt;
&lt;p&gt;No default constructors, copy constructors or assignment operators needed.  Actually, I don’t know of any other language than C&#x2F;C++ where you can have arrays with your own objects as value types.&lt;&#x2F;p&gt;
&lt;p&gt;However, I wouldn’t argue that you’re wrong.  C++ is an arcane and complex language.  You can gain a little efficiency and direct control of the computer hardware, but it is rarely worth all the problems.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    The rant rambler
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;The non-C++ examples are a perfect illustration why there shouldn’t be a C++ newbie anymore on this planet.&lt;br&#x2F;&gt;
Like, if you know everything about the inner workings of a combustion engine would that make you a better driver?&lt;br&#x2F;&gt;
In my 20 years as a C programmer I’ve seen worse C++ programmers (and they weren’t newbies) than C ones.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ryan Walker
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;in ruby, one line:&lt;&#x2F;p&gt;
&lt;p&gt;arr = []&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Leonardo
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;ArrayList arr = Arrays.asList(new MyObj());&lt;&#x2F;p&gt;
&lt;p&gt;And still… strongly typed, compiler checked…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ryan Walker
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;in ruby, one line:&lt;&#x2F;p&gt;
&lt;p&gt;arr = [] &amp;lt;&amp;lt; Object.new&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Leonardo
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wooops… to fast with the copy’n’paste. Should be:&lt;&#x2F;p&gt;
&lt;p&gt;List arr = Arrays.asList(new MyObj());&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Tac-Tics&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;arr = [MyObj()]&lt;&#x2F;blockquote&gt;
&lt;p&gt;I was expecting to use the MyObj later, and&#x2F;or do some more stuff to it before I added it to the array.  I write quite a lot of Python, so obviously at least one people does use this kind of style.  I admit it is probably quite Java-y.&lt;&#x2F;p&gt;
&lt;blockquote&gt;The Haskell code is more less good.&lt;&#x2F;blockquote&gt;
&lt;p&gt;:) The Haskell example was added for fun and is my first ever line of Haskell.  Sounds like I made quite a few mistakes.  I have corrected it based on another comment.  Please let me know if the corrected version is wrong in any way.&lt;&#x2F;p&gt;
&lt;blockquote&gt;you should probably be more careful if youâ€™re going to make a point about language inexperience&lt;&#x2F;blockquote&gt;
&lt;p&gt;Point taken :).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;T4C&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;itâ€™s a volume of details, not concepts that are required&lt;&#x2F;blockquote&gt;
&lt;p&gt;I think there is a large number of very complex concepts too – more than most languages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;markus&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;But one problem is that, with languages like Ruby and Python becoming more popular, and C still being the oldschool powerhorse - why is the complexity inside C++ needed?&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ruby or Python with C for the fast parts is a compelling alternative in some situations, but C++ can really express some things you can’t do in other languages… and isn’t it just more exciting to code in?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;noah&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;Thanks, yes, I was assuming I’d be doing something to the MyObj before putting it into the array.  I don’t know why I was assuming that…&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;kL&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;array ~= obj;&lt;&#x2F;blockquote&gt;
&lt;p&gt;Wow, cool operator!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Riaan Minne&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;the idea that you have to understand how things work under the hood of a language should be applied to any language&lt;&#x2F;blockquote&gt;
&lt;p&gt;My argument is that in C++ it is &lt;em&gt;necessary&lt;&#x2F;em&gt; to know how things work before you even start.  Of course, one can become an expert in any programming language, and yes, many of the concepts you acquire are transferrable.  By no means all, though.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;rrwood&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;I suspect that there are cases where C++ really makes sense&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, and I’m not at all saying you shouldn’t use it – I’m mainly saying you should make sure you hire gurus, I think.  Actually, I’m not completely sure what I am saying you should do – I’m basically just saying that C++ is for experts.&lt;&#x2F;p&gt;
&lt;p&gt;Incidentally, one compelling area for using C++ is embedded.  But I think it can be great for anything.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Err&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;You donâ€™t have to know how the STL is implemented, because the requirements on the type traits of vector elements are specified in the standard.&lt;&#x2F;blockquote&gt;
&lt;p&gt;That is technically true.  However, the only way I can think of to remember those otherwise arbitrary restrictions is to have some kind of mental model of what is going on underneath.  That’s certainly how I remember them (when I do).  If you prefer to remember them by memorising the standard, then my argument is simply that there is a large amount of arbitrary information which needs to be learnt before you use STL containers.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Anon&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;do you mean ArrayList&lt;&#x2F;blockquote&gt;
&lt;p&gt;Thank you, yes, I am an idiot.  Example corrected.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;erm….&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;if people are not going to hire newbs, how are they going to become !newbs ?&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, that is a major problem.  I started learning C++ during my PhD, which helped because I had no employer as such, but I have become a lot better (certainly not expert…) since I started using it for my job.  I am very grateful to my employer for taking me on, but very regretful about a lot of the code I wrote in the past – I wish I had been mentored much more.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Ric&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;Like the C example, the PErl can be simplified from this:
...snip...
To this:
my $obj = MyObj-&amp;gt;new();
my @arr = ( $obj );&lt;&#x2F;blockquote&gt;
&lt;p&gt;Simplified or shortened?  I prefer the longer version to clarify what each step does (especially in an example…).&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;fname lname&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;DIM a$&lt;br&#x2F;&gt;
INPUT &quot;Enter your name&quot;, a$&lt;br&#x2F;&gt;
PRINT &quot;Hello, &quot;, a$&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Mike&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;Your example cites only references to the Standard Template Library, which is not an integral part of the C++ language itself.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Since it’s part of the standard, I would argue that it is.&lt;&#x2F;p&gt;
&lt;blockquote&gt;I would agree that the STL is overly complex&lt;&#x2F;blockquote&gt;
&lt;p&gt;It remains an open question as to whether it is possible to implement a powerful library in C++ that does not require you to have a good feel for how it works under the covers before you use it.  Maybe someone can suggest an example of such a library.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps the compromise would be in terms of performance, which would take away a lot of people’s reasons for using C++ in the first place.&lt;&#x2F;p&gt;
&lt;p&gt;BTW, I never said that STL is overly complex, only that it is complex.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Edgardo Portal&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;The three things you note about C++ are about the first thing most books mention (along with providing a (possibly virtual) destructor), so Iâ€™d venture to guess that most people venturing to use the STL would have that under their belts by then.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I certainly didn’t have this under my belt when I first used STL containers – I just got scared and put pointers into STL containers and had to manage the memory myself.  Understanding by-value semantics came later for me.  Perhaps it was because I didn’t really read books.  (I’m trying to correct that now.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Sgt. Pepper&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;Another stupid and pointless C++ flameâ€¦&lt;&#x2F;blockquote&gt;
&lt;p&gt;It’s not a flame – I love C++!&lt;&#x2F;p&gt;
&lt;p&gt;I hope it’s not pointless – maybe it will help us think about things like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;How to teach C++?  (e.g. Charles Bailey and I had an interesting chat today about whether you can teach C++ by starting with by-value semantics and only covering pointers later.  Hopefully he’ll blog about that, along with the myriad ways he disagrees with me :)&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;How to write libraries?&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;p&gt;The need for mentoring of new developers?&lt;&#x2F;p&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Max&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;To me it sounds more like you are saying C++ is a language for people that have actually used it for longer than a week.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I have used it for several years and have loads left to learn.  I also know people who are way, way stronger than me and they say they have a lot left to learn as well.  You could be an incredibly fast learner.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;B.G.&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;The truth is, most developers writing C++ code are doing so using MFC, or the Borland equivalent (C++ Builder) - especially in Europe.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I work as a programmer in Europe and I use STL whenever I can, which is a lot.  All the underlying classes we use company-wide are based on STL templates and algorithms, where they are appropriate.&lt;&#x2F;p&gt;
&lt;p&gt;STL is excellent!  I’ve heard that MFC is horrible, but I’ve never used it.&lt;&#x2F;p&gt;
&lt;p&gt;One reason to use the standard facilities is portability.  Surely for &quot;Enterprise&quot; software this is important?  Our product runs on 4 platforms, only one of which has MFC.&lt;&#x2F;p&gt;
&lt;p&gt;Lots of people at last week’s ACCU conference were very interested in STL, so some people are definitely using it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;This post reads as old-school, pretentious, bullcrap! Whatâ€™s next - every car should have a Porsche engine? Every medical procedure MUST be carried out in the number 1 US hospital? Every wife should appear in Maximâ€™s top 100 list before the ring is offered, otherwise theyâ€™re too ugly and should be dumped at the alter?&lt;&#x2F;blockquote&gt;
&lt;p&gt;I don’t understand your point.  Please could you be more explicit?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Kyle&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;Granted, the compiler will let you CREATE a class without these, but even a C++ newbie, even somebody whoâ€™s had a single class in the language, knows that every class in C++ must have a default constructor, a copy constructor, and an assignment operator.&lt;&#x2F;blockquote&gt;
&lt;p&gt;No, every class with value semantics needs these.  John Lakos’ talk &quot;Toward a Common Intuition and Reusable Testing Methodology&quot; at the ACCU conference (which inspired my use of this example here) was very interesting on this topic.  I don’t know whether he has put his slides online – they might be interesting, but really you’d need to see the video to get the full force of it!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Misc&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;Yeah .. You need to know what youâ€™re doing, which is completely unlike every other language on the planet.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I presume this is meant sarcastically.  I think there are degrees of &quot;knowing what you’re doing&quot;, and I think C++ requires a higher degree.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Karl&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;the details you cited as things you have to know about how it works have nothing to do with the implementation of std::vector, they are only parts of the complicated interface of std::vector.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, agreed in theory.  As I mentioned in another comment, though, in practice, the only way I have found to remember such details is to think (in general terms) about how the artefacts I am using are implemented.  Stroustrup encourages this approach, probably partly because it is a book intended to help those who implement the language as well as those who use it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;If you want to argue that C++ occupies an entirely separate &amp;#34;complication class,&amp;#34; you will need a different argument.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I agree that it is perfectly possible to remember requirements like these without thinking about the implementation, but I can’t think of an easier way.&lt;&#x2F;p&gt;
&lt;p&gt;I think I’d be happy to say that C++ is more complex than other languages, rather than belonging to a separate class.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Frank&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;Bad programmers are detrimental, regardless of the language.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Agreed.  What about inexperienced coders?  How damaging are they?  How easy is it to spot their errors?&lt;&#x2F;p&gt;
&lt;blockquote&gt;The issue raised in this article is less about cpp and more about garbage collection&#x2F;reference counting.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I’d say it was a more general point than that.  In general I have found that people documenting C++ libraries talk about the implementation (in general terms) quite often, whereas I have found that that is much more rare when documenting libraries for other languages.  Perhaps it is purely a cultural issue.&lt;&#x2F;p&gt;
&lt;blockquote&gt;Pascal used array of int, nothing complex there in cpp either&lt;&#x2F;blockquote&gt;
&lt;p&gt;My point here is that Pascal is simpler.  If you want more complex semantics you have to write them yourself.&lt;&#x2F;p&gt;
&lt;blockquote&gt;C example uses value type copied by value. Ownership of members is â€˜undefinedâ€™, a crash waiting to happen.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, if your struct contains pointers.&lt;&#x2F;p&gt;
&lt;blockquote&gt;Most of the CPP errors result from passing the object by value. The compiler is warning you that it canâ€™t do it correctly without being told how.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, the existence (and use in STL) of by-value semantics in C++ is an example of one of the ways it is more complex than less flexible languages.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;raincat&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;To C++â€™s defence here: the program you have written here is semantically very different from the other languages.&lt;&#x2F;blockquote&gt;
&lt;blockquote&gt;Actually, I donâ€™t know of any other language than C&#x2F;C++ where you can have arrays with your own objects as value types.&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yes, that is exactly my point – having these difficult to understand facilities available makes C++ complex.&lt;&#x2F;p&gt;
&lt;blockquote&gt;However, I wouldnâ€™t argue that youâ€™re wrong. C++ is an arcane and complex language. You can gain a little efficiency and direct control of the computer hardware, but it is rarely worth all the problems.&lt;&#x2F;blockquote&gt;
&lt;p&gt;I think I really must have put myself across badly if it sounded like I was criticising C++.&lt;&#x2F;p&gt;
&lt;p&gt;I agree that C++ is complex, and sometimes arcane, but it is also an amazing phenomenon, full of power and beauty, and incredibly useful for almost anything.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Glen Goldsmith
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Wow.  Good info.&lt;&#x2F;p&gt;
&lt;p&gt;Are there some good books that discuss this type of thing ?  I’ve had a couple of C++ classes at college… and you know I’ve never run into that information before.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for the heads up….&lt;br&#x2F;&gt;
Glen&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Glen, glad to hear you found it useful.  As someone else pointed out, don’t forget about the destructor (although if you don’t have any raw pointer members, the compiler-provided one may well be fine).&lt;&#x2F;p&gt;
&lt;p&gt;I use &lt;a href=&quot;http:&#x2F;&#x2F;www.research.att.com&#x2F;~bs&#x2F;3rd.html&quot; rel=&quot;nofollow&quot;&gt;Stroustrup&lt;&#x2F;a&gt;, but I’m not convinced it is the best place to start.  Whenever anyone asks me about books I usually just say &quot;always choose an O’Reilly&quot;, but in this case I happen to think that &lt;a href=&quot;http:&#x2F;&#x2F;www.oreilly.com&#x2F;catalog&#x2F;cplus2&#x2F;&quot; rel=&quot;nofollow&quot;&gt;Practical C++ Programming&lt;&#x2F;a&gt; is not that good (it’s the book I used to learn, and I didn’t find it inspiring).&lt;&#x2F;p&gt;
&lt;p&gt;This one looks quite cool: &lt;a href=&quot;http:&#x2F;&#x2F;www.oreilly.com&#x2F;catalog&#x2F;1886411956&#x2F;&quot; rel=&quot;nofollow&quot;&gt;How Not to Program in C++&lt;&#x2F;a&gt;, but I’ve never read it.&lt;&#x2F;p&gt;
&lt;p&gt;If you want to keep going deeper, I’ve found some really interesting stuff on the &lt;a href=&quot;http:&#x2F;&#x2F;www.ddj.com&#x2F;cpp&#x2F;&quot; rel=&quot;nofollow&quot;&gt;Dr Dobb’s Journal&lt;&#x2F;a&gt; web site, and I can recommend &lt;a href=&quot;http:&#x2F;&#x2F;accu.org&#x2F;&quot; rel=&quot;nofollow&quot;&gt;ACCU&lt;&#x2F;a&gt;’s publications Overload and CVu.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;www.aristeia.com&#x2F;&quot; rel=&quot;nofollow&quot;&gt;Scott Meyers&lt;&#x2F;a&gt;’ books &quot;Effective C++&quot; and &quot;More Effective C++&quot; are excellent, but not aimed at beginners.  They cover exactly the type of thing I described above in this blog entry.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps others have suggestions for good books they used to learn C++?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Justin
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;MFC does suck.  And sucks hard (at least back in 2001 when I had to use it).&lt;&#x2F;p&gt;
&lt;p&gt;As far as understanding the under-workings of the STL, I think, instead, that a basic understanding of how different data structures work and where they are appropriate is sufficient, especially since, as previously pointed out, that the STL is implemented differently all over the place.&lt;&#x2F;p&gt;
&lt;p&gt;Also, PHP:&lt;&#x2F;p&gt;
&lt;p&gt;$arr = array( new MyObj() );&lt;&#x2F;p&gt;
&lt;p&gt;and JavaScript:&lt;&#x2F;p&gt;
&lt;p&gt;var MyObj = {}, arr = [ MyObj ];&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jlnr
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-07&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Article- I agree that many details make this complicated, but it’s not &lt;em&gt;that&lt;&#x2F;em&gt; fundamentally bad. For most of the other languages, you inevitably have to understand the difference between references and values; Ruby tries to turn everything into a reference, but if you want to be clever and create a list of ten empty strings via [&quot;&quot;] * 10, the fundamental problem comes up again as soon as you try to modify one of them. In Java you also have to understand the (annoying) difference between primitives and objects.&lt;br&#x2F;&gt;
But once you understand to divide your classes into value types and noncopyable reference types, and use tr1 pointers to handle the reference types, it gets so much easier and the whole design suddenly makes a lot of sense, though technically, even more things are happening under the surface.&lt;&#x2F;p&gt;
&lt;p&gt;I don’t understand why everything surrounding copying is brought up so often, while I haven’t read much criticism for completely failed concepts such as argument dependent lookup, which is PHP-class bad in that it makes a seemingly harmless feature (calling a function) overly complex. Templates being complex is just something you should expect ;)&lt;&#x2F;p&gt;
&lt;p&gt;Kyle- Totally disagree. Define copying&#x2F;assignment at the lowest level, i.e. smart pointers, containers etc., then just do nothing in the upper levels. Things owning an object that can’t be copied have a scoped_ptr member so aren’t copyable (rightly so!), structs that only have some strings and ints can be copied. Problem solved ;)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Glen Goldsmith
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hey Andy…&lt;&#x2F;p&gt;
&lt;p&gt;THanks so much.&lt;&#x2F;p&gt;
&lt;p&gt;The Scott Meyers’ books are probably what I’m looking for.  After taking a few C++ classes at school, I’ve been through several books — and very important information  such as the above, aren’t covered in beginning books.&lt;&#x2F;p&gt;
&lt;p&gt;Most of these beginning books cover syntax, a little OOP theory, a dash of UML 2 and after 800 pages and 15 chapters…  congrats!  You can put C++ on your resume now!&lt;&#x2F;p&gt;
&lt;p&gt;So this is very helpful stuff.&lt;&#x2F;p&gt;
&lt;p&gt;The web of course is an extremely helpful tool —– but to answer specific questions without knowing why, it’s great.  On example:  What’s the fastest way to copy very large amounts of small files.  The web has that answer, but usually never &quot;why&quot; the code is that answer.  They don’t go into the depth and background you need to know… to get some experience so you can take that knowledge forth and prosper.&lt;&#x2F;p&gt;
&lt;p&gt;Keep up the great blog.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ben
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;In Ruby:&lt;&#x2F;p&gt;
&lt;p&gt;arr = [MyObj.new]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    kg
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;At college I was taught how to program using C++, but I didn’t really &lt;em&gt;learn&lt;&#x2F;em&gt; C++ till I read C++ Primer (4th ed) by Stan Lippman, cover-to-cover.  I think it’s probably the best book for learning the fundamentals and how to use C++ &quot;the right way&quot;.  Admittedly, it’s a very long read, but it’s a good place to start if you want to grok C++ instead of just getting by.&lt;&#x2F;p&gt;
&lt;p&gt;Stoustrup’s book seems more focused on the language itself and less focused on how to use it.  But, if you want to know why C++ is the way it is (although probably most people don’t), check out his other book The Design and Evolution of C++.&lt;&#x2F;p&gt;
&lt;p&gt;Nowadays, I have to consult the C++ standard itself once in a while (the only way to get a definitive answer on some very specific questions).  ANSI sells it for about $30 (which is still highway robbery for a language standard).  Don’t be afraid to get yourself a copy and have it as a reference.&lt;&#x2F;p&gt;
&lt;p&gt;I haven’t read the Scott Meyers books, but they are probably very good, not so much for learning the basics, but for how to solve the kinds of problems that come up a lot when using C++ in the real world.  For this type of thing, also check out Herb Sutter’s books and his Guru of the Week articles.&lt;&#x2F;p&gt;
&lt;p&gt;In any case, you are definitely right, C++ is geared more toward experts.  Hopefully C++0x will change that somewhat.  However, I’m of the opinion that becoming a C++ expert is worth the effort.  (Can you be an expert user of a language without being an expert in the language itself?)&lt;&#x2F;p&gt;
&lt;p&gt;Warning: Use of RAII may cause your debugging skills to falter, because you won’t need to use the debugger as often.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Enlaces del 28&amp;#x2F;04&amp;#x2F;2008 | El ChigÃ¼ire Literario
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] quÃ© se usa C++ en los videojuegos? y C++ es un lenguaje para expertos. Enlaces robados a […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    GCC&amp;#x27;s Unhelpful Error Messages | Cardinal Peak
    &lt;span class=&quot;comment_date&quot;&gt;2019-04-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Balaam has a humorous blog post whereâ€”almost as an asideâ€”he generates a 1950-byte error message from a source program of only […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    GCC’s Unhelpful Error Messages – Cardinal Peak
    &lt;span class=&quot;comment_date&quot;&gt;2019-12-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Balaam has a humorous blog post whereâ€”almost as an asideâ€”he generates a 1950-byte error message from a source program of only […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Templated test code?</title>
          <pubDate>Wed, 19 Mar 2008 10:51:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/03/19/templated-test-code/</link>
          <guid>https://artificialworlds.net/blog/2008/03/19/templated-test-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/03/19/templated-test-code/">&lt;p&gt;At work at the moment, as part of an initiative to get with the 21st century, we are waking up to testing our code.&lt;&#x2F;p&gt;
&lt;p&gt;Thus, I am writing a lot of unit tests for old code, which can be soul-destroyingly repetitive and very pointless-feeling (even though really I do see a great value in the end result - tested code is refactorable code).&lt;&#x2F;p&gt;
&lt;p&gt;Often, tests have a lot in common with each other, so it feels right to reduce code repetition, and factor things into functions etc. The Right Way of doing this is to leave your tests as straightforward as possible, with preferably no code branches at all, just declarative statements.&lt;&#x2F;p&gt;
&lt;p&gt;Contemplating writing unit tests for the same method on 20+ very similar classes, using a template function &quot;feels&quot; right, for normal code values of &quot;feel&quot;. However, for test code, maybe it&#x27;s wrong?&lt;&#x2F;p&gt;
&lt;p&gt;My question is: is it ok to write a test function like this?:&lt;&#x2F;p&gt;
&lt;pre&gt;void test_all_thingies()
{
    test_One_Thingy&amp;lt;Thingy1&amp;gt;();
    test_One_Thingy&amp;lt;Thingy2&amp;gt;();
    test_One_Thingy&amp;lt;Thingy3&amp;gt;();
    test_One_Thingy&amp;lt;Thingy4&amp;gt;();
}

template&amp;lt; class T &amp;gt;
void test_One_Thingy()
{
    T thingy;
    thingy.doSomething();
    TEST_ASSERT( thingy.isSomething() );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Worse still, is this ok?&lt;&#x2F;p&gt;
&lt;pre&gt;void test_all_thingies()
{
    test_One_Thingy&amp;lt;Thingy1&amp;gt;( &quot;Thingy1 expected output&quot; );
    test_One_Thingy&amp;lt;Thingy2&amp;gt;( &quot;Thingy2 expected output&quot; );
    test_One_Thingy&amp;lt;Thingy3&amp;gt;( &quot;Thingy3 expected output&quot; );
    test_One_Thingy&amp;lt;Thingy4&amp;gt;( &quot;Thingy4 expected output&quot; );
}

template&amp;lt; class T &amp;gt;
void test_One_Thingy( std::string expected_output )
{
    T thingy;
    thingy.doSomething();
    TEST_ASSERT( thingy.getOutput() == expected_output );
}
&lt;&#x2F;pre&gt;
&lt;p&gt;Reasons for: otherwise I&#x27;m going to be writing huge amounts of copy-pasted code (unless someone can suggest a better way?).&lt;&#x2F;p&gt;
&lt;p&gt;Reasons against: how clear is it going to be &lt;em&gt;which&lt;&#x2F;em&gt; class failed the test when it fails?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: fixed unescaped diagonal brackets.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I presume a clerical error has left out the template parameter specification on the test_One_Thingy function calls.&lt;&#x2F;p&gt;
&lt;p&gt;‘Reasons against’ can be rebutted fairly fast. The thing with templates is that they are ‘real code’, so you can’t use any preprocessor code in the template function to distinguish different instantiations.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming that TEST_ASSERT is a macro that makes use of &lt;strong&gt;FILE&lt;&#x2F;strong&gt; and &lt;strong&gt;LINE&lt;&#x2F;strong&gt; for its helpful diagnostic, the obvious way is to make a more configurable macro which does essentially the same thing, but allows you to specify the file and line. For example, make TEST_ASSERT_FL macro so that these two lines are equivalent.&lt;br&#x2F;&gt;
TEST_ASSERT_FL( x, &lt;strong&gt;FILE&lt;&#x2F;strong&gt;, &lt;strong&gt;LINE&lt;&#x2F;strong&gt; )&lt;br&#x2F;&gt;
TEST_ASSERT( x )&lt;&#x2F;p&gt;
&lt;p&gt;Then, change the signature of test_One_Thingy as follows.&lt;br&#x2F;&gt;
template void test_One_Thingy( const std::string&amp;amp; ex, const char* file, unsigned int line )&lt;&#x2F;p&gt;
&lt;p&gt;And have it use the new macro.&lt;br&#x2F;&gt;
TEST_ASSERT_FL( thingy.getOutput() == expected_output, file, line );&lt;&#x2F;p&gt;
&lt;p&gt;Then create a new macro.&lt;br&#x2F;&gt;
#defined TEST_ONE_THINGY( cls, expect ) test_One_Thingy( expect, &lt;strong&gt;FILE&lt;&#x2F;strong&gt;, &lt;strong&gt;LINE&lt;&#x2F;strong&gt; )&lt;&#x2F;p&gt;
&lt;p&gt;void test_all_thingies()&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
TEST_ONE_THINGY( ClassA, &quot;Thingy1 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( ClassB, &quot;Thingy2 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( ClassC, &quot;Thingy3 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( ClassD, &quot;Thingy4 expected output&quot; );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;File and line now give you which class caused the failure, at the expense of not telling you the file and line of the template code where the actual test is. (This may not matter much if the template code only has a single assert macro.)&lt;&#x2F;p&gt;
&lt;p&gt;Now the funky way :) .&lt;&#x2F;p&gt;
&lt;p&gt;Assuming you have a TestAssert underlying function on which the TEST_ASSERT macro is built…&lt;&#x2F;p&gt;
&lt;p&gt;template&lt;br&#x2F;&gt;
inline std::string SuffixType( const std::string&amp;amp; in )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
std::ostringstream s;&lt;br&#x2F;&gt;
s ( #x ).c_str(), &lt;strong&gt;FILE&lt;&#x2F;strong&gt;, &lt;strong&gt;LINE&lt;&#x2F;strong&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;template&lt;br&#x2F;&gt;
void test_One_Thingy( const std::string&amp;amp; expected_output )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
T thingy;&lt;br&#x2F;&gt;
thingy.doSomething();&lt;br&#x2F;&gt;
TEST_ASSERT_TEMPL( T, thingy.getOutput() == expected_output );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;Warning! The type suffix is not guaranteed to be human readable.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Grrrr, WordPress has completely garabled all of the angle brackets from my comments.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I think I get the idea, and it certainly helps us tell which test fails, but it still feels a little complicated for test code, which I should have added as a further reason against.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Charles emailed the ungarbled version to me:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;&#x2F;p&gt;&lt;pre&gt;&lt;br&#x2F;&gt;
#include &quot;hshgtest.h&quot;&lt;br&#x2F;&gt;
#include &lt;br&#x2F;&gt;
#include &lt;p&gt;&lt;&#x2F;p&gt;
&lt;p&gt;struct ClsA&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
const char* str() const { return &quot;My name is A&quot;; }&lt;br&#x2F;&gt;
};&lt;&#x2F;p&gt;
&lt;p&gt;struct ClsB&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
const char* str() const { return &quot;My name is B&quot;; }&lt;br&#x2F;&gt;
};&lt;&#x2F;p&gt;
&lt;p&gt;struct ClsC&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
const char* str() const { return &quot;My Name is C&quot;; }&lt;br&#x2F;&gt;
};&lt;&#x2F;p&gt;
&lt;p&gt;struct ClsD&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
const char* str() const { return &quot;My name is D&quot;; }&lt;br&#x2F;&gt;
};&lt;&#x2F;p&gt;
&lt;p&gt;namespace&lt;br&#x2F;&gt;
{&lt;&#x2F;p&gt;
&lt;p&gt;template&amp;lt; class T &amp;gt;&lt;br&#x2F;&gt;
inline std::string SuffixType( const std::string&amp;amp; in )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
std::ostringstream s;&lt;br&#x2F;&gt;
s &amp;lt;&amp;lt; in &amp;lt;&amp;lt; &quot; : &quot; &amp;lt;&amp;lt; typeid( T ).name();&lt;br&#x2F;&gt;
return s.str();&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;#define TEST_ASSERT_TEMPL( t, x ) HSHGTest::TestAssert( x, SuffixType&amp;lt; t &amp;gt;( #x ).c_str(), &lt;strong&gt;FILE&lt;&#x2F;strong&gt;, &lt;strong&gt;LINE&lt;&#x2F;strong&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;template&amp;lt; class T &amp;gt;&lt;br&#x2F;&gt;
void ClassTest( const std::string&amp;amp; expect )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
T test;&lt;br&#x2F;&gt;
TEST_ASSERT_TEMPL( T, expect == test.str() );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;void testall()&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
ClassTest&amp;lt; ClsA &amp;gt;( &quot;My name is A&quot; );&lt;br&#x2F;&gt;
ClassTest&amp;lt; ClsB &amp;gt;( &quot;My name is B&quot; );&lt;br&#x2F;&gt;
ClassTest&amp;lt; ClsC &amp;gt;( &quot;My name is C&quot; );&lt;br&#x2F;&gt;
ClassTest&amp;lt; ClsD &amp;gt;( &quot;My name is D&quot; );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;}&lt;&#x2F;p&gt;
&lt;p&gt;HSHG_BEGIN_TESTS&lt;br&#x2F;&gt;
HSHG_TEST_ENTRY( testall )&lt;br&#x2F;&gt;
HSHG_END_TESTS&lt;&#x2F;p&gt;
&lt;p&gt;HSHG_TEST_MAIN&lt;br&#x2F;&gt;
&lt;&#x2F;p&gt;&lt;&#x2F;pre&gt;&lt;p&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;What about just plain old this?:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;&#x2F;p&gt;&lt;pre&gt;&lt;p&gt;&lt;&#x2F;p&gt;
&lt;p&gt;#define TEST_ONE_THINGY(CLS,OUTPUT) test_One_Thingy( #CLS, OUTPUT );&lt;&#x2F;p&gt;
&lt;p&gt;void test_all_thingies()&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
TEST_ONE_THINGY( Thingy1, &quot;Thingy1 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( Thingy2, &quot;Thingy2 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( Thingy3, &quot;Thingy3 expected output&quot; );&lt;br&#x2F;&gt;
TEST_ONE_THINGY( Thingy4, &quot;Thingy4 expected output&quot; );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;template&amp;lt; class T &amp;gt;&lt;br&#x2F;&gt;
void test_One_Thingy( std::string class_name, std::string expected_output )&lt;br&#x2F;&gt;
{&lt;br&#x2F;&gt;
T thingy;&lt;br&#x2F;&gt;
thingy.doSomething();&lt;br&#x2F;&gt;
TEST_ASSERT_CLS( class_name, thingy.getOutput() == expected_output );&lt;br&#x2F;&gt;
}&lt;&#x2F;p&gt;
&lt;p&gt;&lt;&#x2F;p&gt;&lt;&#x2F;pre&gt;&lt;p&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Where TEST_ASSERT_CLS does the obvious thing.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-03-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Stringificizationalism seems the most reliable way to get the class name here.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Public bzr branch of FreeGuide</title>
          <pubDate>Wed, 12 Mar 2008 09:56:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/03/12/public-bzr-branch-of-freeguide/</link>
          <guid>https://artificialworlds.net/blog/2008/03/12/public-bzr-branch-of-freeguide/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/03/12/public-bzr-branch-of-freeguide/">&lt;p&gt;On the subject of distributed source code management, Dan Watkins has just informed me that the launchpad team have created a bazaar branch of FreeGuide&#x27;s code, so if you&#x27;re into that kind of thing, you can download the code from that instead of our central subversion repo.&lt;&#x2F;p&gt;
&lt;p&gt;The link is here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;code.edge.launchpad.net&#x2F;~vcs-imports&#x2F;freeguide-tv&#x2F;trunk&quot;&gt;http:&#x2F;&#x2F;code.edge.launchpad.net&#x2F;~vcs-imports&#x2F;freeguide-tv&#x2F;trunk&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re into git, I&#x27;d suggest git-svn. It&#x27;s what I use for FreeGuide development now. Let me know if you have trouble getting it working.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Public git repo for GSSMP</title>
          <pubDate>Wed, 12 Mar 2008 09:30:39 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/03/12/public-git-repo-for-gssmp/</link>
          <guid>https://artificialworlds.net/blog/2008/03/12/public-git-repo-for-gssmp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/03/12/public-git-repo-for-gssmp/">&lt;p&gt;Git is supposed to work when you upload your repository to an http server, but in reality, no-one except me seems to use it, and it&#x27;s nothing like as useful as having a proper repository that people can commit to etc.&lt;&#x2F;p&gt;
&lt;p&gt;So, here it is: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;repo.or.cz&#x2F;w&#x2F;gssmp.git&quot;&gt;http:&#x2F;&#x2F;repo.or.cz&#x2F;w&#x2F;gssmp.git&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Finally released last.fm support for GSSMP</title>
          <pubDate>Sun, 20 Jan 2008 09:34:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2008/01/20/finally-released-lastfm-support-for-gssmp/</link>
          <guid>https://artificialworlds.net/blog/2008/01/20/finally-released-lastfm-support-for-gssmp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2008/01/20/finally-released-lastfm-support-for-gssmp/">&lt;p&gt;There was a weird bug in my last.fm support for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;GSSMP&lt;&#x2F;a&gt;, where it would crash when run from the GNOME panel, but be fine when run from a terminal.&lt;&#x2F;p&gt;
&lt;p&gt;After two bugs fixes from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;ccgi.hashpling.plus.com&#x2F;blog&#x2F;&quot;&gt;Charles Bailey&lt;&#x2F;a&gt;, the problem seems to have gone away, so I&#x27;ve finally been able to make a release.&lt;&#x2F;p&gt;
&lt;p&gt;Now I have to resist the temptation to choose songs to make last.fm think I&#x27;m cool, instead of songs I actually want to listen to.&lt;&#x2F;p&gt;
&lt;p&gt;And I really need to fix the debian&#x2F;Ubuntu package of lastfmsubmitd so that the client exe lastfmsubmit is included in the package. Without that, using last.fm with GSSMP is a major hassle.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>lighttpd on Ubuntu - alias.url doesn&#x27;t work when included in a module config file</title>
          <pubDate>Sun, 09 Dec 2007 01:00:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/12/09/lighttpd-on-ubuntu-aliasurl-doesnt-work-when-included-in-a-module-config-file/</link>
          <guid>https://artificialworlds.net/blog/2007/12/09/lighttpd-on-ubuntu-aliasurl-doesnt-work-when-included-in-a-module-config-file/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/12/09/lighttpd-on-ubuntu-aliasurl-doesnt-work-when-included-in-a-module-config-file/">&lt;p&gt;I&#x27;ve decided to use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.lighttpd.net&#x2F;&quot;&gt;lighttpd&lt;&#x2F;a&gt; on my new machine (which was generously given to me and features a 700GB raid array).&lt;&#x2F;p&gt;
&lt;p&gt;My main reason for choosing lighttpd was the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;trac.lighttpd.net&#x2F;trac&#x2F;wiki&#x2F;Docs%3AModFLVStreaming&quot;&gt;FLV streaming plugin&lt;&#x2F;a&gt; for it that will make my Wii TV viewing experience a lot better. (It works - I&#x27;ve tried it!)&lt;&#x2F;p&gt;
&lt;p&gt;However, if I&#x27;m going to use lighttpd for streaming TV to my Wii, I want to use it for everything, rather than having Apache running as well. In any case, I think given my load pattern (average 0.01 users at any time, approx) a lower-memory (but maybe less stable under heavy load) solution seems a good choice.&lt;&#x2F;p&gt;
&lt;p&gt;So I have to get my webmail, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;myaddressbook.sourceforge.net&#x2F;&quot;&gt;home-grown address book&lt;&#x2F;a&gt;, and mediawiki installations all working with lighttpd. How hard can it be?&lt;&#x2F;p&gt;
&lt;p&gt;Actually, harder than it should be.&lt;&#x2F;p&gt;
&lt;p&gt;Lighttpd is supposed to listen to a directive that looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;alias.url += ( &quot;&#x2F;mediawiki&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;mediawiki1.10&#x2F;&quot; )&lt;&#x2F;pre&gt;
&lt;p&gt;and on Ubuntu, being Debian-derived, you can split your lighttpd config into separate files, allowing apt to modify the main config file during an upgrade without blatting your customisations.&lt;&#x2F;p&gt;
&lt;p&gt;So I created a file in &#x2F;etc&#x2F;lighttpd&#x2F;conf-available&#x2F; called 50-mediawiki.conf that contained the alias-url line above, and then I ran:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo lighty-enable-mod mediawiki&lt;&#x2F;pre&gt;
&lt;p&gt;and a symbolic link was duly created at &#x2F;etc&#x2F;lighttpd&#x2F;conf-enabled&#x2F;50-mediawiki.conf.&lt;&#x2F;p&gt;
&lt;p&gt;I restarted lighttpd like so:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo &#x2F;etc&#x2F;init.d&#x2F;lighttpd force-reload&lt;&#x2F;pre&gt;
&lt;p&gt;and navigated to localhost&#x2F;mediawiki, but got a 404 error.&lt;&#x2F;p&gt;
&lt;p&gt;A lot of experimentation later, and it turns out to be a stupid thing.&lt;&#x2F;p&gt;
&lt;p&gt;Lighttpd has a stupid &quot;by design&quot; behaviour, which I think I can summarise as &quot;if you encounter a change to alias.url that comes AFTER a conditional that modifies alias.url, ignore it.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;So, to explain by example, this code in &#x2F;etc&#x2F;lighttpd&#x2F;lighttpd.conf will successfully redirect &#x2F;test&#x2F; to &#x2F;home&#x2F;andy&#x2F;test&#x2F;:&lt;&#x2F;p&gt;
&lt;pre&gt;alias.url += ( &quot;&#x2F;test&#x2F;&quot; =&amp;gt; &quot;&#x2F;home&#x2F;andy&#x2F;test&#x2F;&quot; )

$HTTP[&quot;remoteip&quot;] == &quot;127.0.0.1&quot; {
        alias.url += (
                &quot;&#x2F;doc&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;doc&#x2F;&quot;,
                &quot;&#x2F;images&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;images&#x2F;&quot;
        )
        $HTTP[&quot;url&quot;] =~ &quot;^&#x2F;doc&#x2F;|^&#x2F;images&#x2F;&quot; {
                dir-listing.activate = &quot;enable&quot;
        }
}
&lt;&#x2F;pre&gt;
&lt;p&gt;but this code:&lt;&#x2F;p&gt;
&lt;pre&gt;$HTTP[&quot;remoteip&quot;] == &quot;127.0.0.1&quot; {
        alias.url += (
                &quot;&#x2F;doc&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;doc&#x2F;&quot;,
                &quot;&#x2F;images&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;images&#x2F;&quot;
        )
        $HTTP[&quot;url&quot;] =~ &quot;^&#x2F;doc&#x2F;|^&#x2F;images&#x2F;&quot; {
                dir-listing.activate = &quot;enable&quot;
        }
}

alias.url += ( &quot;&#x2F;test&#x2F;&quot; =&amp;gt; &quot;&#x2F;home&#x2F;andy&#x2F;test&#x2F;&quot; )
&lt;&#x2F;pre&gt;
&lt;p&gt;Will leave you scratching your head as to why it doesn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;Since the bit of lighttpd.conf that is put there by Debian (or Ubuntu?) to include all files in conf-enabled&#x2F; comes at the very end of the file, any alias.url lines in any module config are ignored. Clever eh?&lt;&#x2F;p&gt;
&lt;p&gt;To skip to the end, my fix was to move the Debian documentation code to the very end of the config file, so the end of lighttpd.conf now looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;...

#### external configuration files
## mimetype mapping
include_shell &quot;&#x2F;usr&#x2F;share&#x2F;lighttpd&#x2F;create-mime.assign.pl&quot;

## load enabled configuration files,
## read &#x2F;etc&#x2F;lighttpd&#x2F;conf-available&#x2F;README first
include_shell &quot;&#x2F;usr&#x2F;share&#x2F;lighttpd&#x2F;include-conf-enabled.pl&quot;

#### handle Debian Policy Manual, Section 11.5. urls
#### and by default allow them only from localhost

$HTTP[&quot;remoteip&quot;] == &quot;127.0.0.1&quot; {
        alias.url += (
                &quot;&#x2F;doc&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;doc&#x2F;&quot;,
                &quot;&#x2F;images&#x2F;&quot; =&amp;gt; &quot;&#x2F;usr&#x2F;share&#x2F;images&#x2F;&quot;
        )
        $HTTP[&quot;url&quot;] =~ &quot;^&#x2F;doc&#x2F;|^&#x2F;images&#x2F;&quot; {
                dir-listing.activate = &quot;enable&quot;
        }
}&lt;&#x2F;pre&gt;
&lt;p&gt;Now my separate file for mediawiki works fine, and my 404 error has changed to a much more healthy 403. (If that causes pain, expect a blog entry on that too.)&lt;&#x2F;p&gt;
&lt;p&gt;As for why lighttpd works in such a stupid way, apparently it&#x27;s by design: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;trac.lighttpd.net&#x2F;trac&#x2F;ticket&#x2F;1427&quot;&gt;lighttpd bug 1427&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;And here&#x27;s the relevant debian bug, to which I will try to remember to reply: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;bugs.debian.org&#x2F;cgi-bin&#x2F;bugreport.cgi?bug=445459&quot;&gt;Debian bug 445459&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Incidentally, running this might be helpful:&lt;&#x2F;p&gt;
&lt;pre&gt;lighttpd -tp -f &#x2F;etc&#x2F;lighttpd&#x2F;lighttpd.conf | less&lt;&#x2F;pre&gt;
&lt;p&gt;It shows you how lighttpd has understood your configuration files.&lt;&#x2F;p&gt;
&lt;p&gt;Lighttpd seems to be light on end-user documentation, and concern for ease of use. I hope I&#x27;m not making a big mistake...&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-12-10&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;lighttpd can’t be too bad, after all it is the default git instaweb solution.  It seems to work for that, although I had difficulty with the current git.  It seems that instaweb isn’t quit insta as I had to poke a config file.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Steve Greenland
    &lt;span class=&quot;comment_date&quot;&gt;2007-12-13&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’ve been mostly happy with lighttpd. The conditional configuration is nifty, the re-write syntax is, I think, easier to deal with than apache’s, and I have no regrets in converting from apache to lighttpd. Yes, the documentation could be better, but the mailling list is pretty responsive, and getting people to write docs is always hard.&lt;&#x2F;p&gt;
&lt;p&gt;But this &quot;design decision&quot; is really weird.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-12-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;It’s good to know that the mailing list is useful – thanks for the input.  I like lighttpd’s config file syntax so far.  Although apache’s style has become familiar, it is pretty obscure.&lt;&#x2F;p&gt;
&lt;p&gt;No arguments about the weird design decision.  I assume it is something that feels natural to the developers because of the way they’ve implemented the config file parsing.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Duncan Idaho’s Weblog » Aliasy, lighttpd i Debian Lenny
    &lt;span class=&quot;comment_date&quot;&gt;2008-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…]  Jak zwykle jestem otwarty na uwagi i propozycje dotyczÄ…ce moich wpisÃ³w. Jako ÅºrÃ³dÅ‚o podam tu Andy Balaam’s Blog, na ktÃ³rym znalazÅ‚em rozwiÄ…zanie tego problemu.  kwiecieÅ„ 20th 2008 Posted to Debian, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>FreeGuide updated to latest XMLTV, and bug fixes</title>
          <pubDate>Fri, 16 Nov 2007 04:03:05 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/11/16/freeguide-updated-to-latest-xmltv-and-bug-fixes/</link>
          <guid>https://artificialworlds.net/blog/2007/11/16/freeguide-updated-to-latest-xmltv-and-bug-fixes/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/11/16/freeguide-updated-to-latest-xmltv-and-bug-fixes/">&lt;p&gt;Check out a release candidate here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;rc&#x2F;&quot;&gt;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;rc&#x2F;&lt;&#x2F;a&gt; .&lt;&#x2F;p&gt;
&lt;p&gt;So far I&#x27;ve only uploaded an RPM, but hopefully there&#x27;ll be a Windows exe and some other packages soon.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update: Windows installer now uploaded too.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Test it! It&#x27;s got better capturing and reporting of download errors, more sensible &quot;download in background&quot; and &quot;show output&quot; behaviour, and its list of XMLTV grabbers is synched with the latest XMLTV (0.5.50).&lt;&#x2F;p&gt;
&lt;p&gt;However, apparently XMLTV 0.5.50 is causing problems in FreeGuide for at least some people, so it may not be a flawless experience...&lt;&#x2F;p&gt;
&lt;p&gt;I really must make a one-button build for Ubuntu .debs, like I have for RPMs. I have been using Ubuntu for some time now...&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d forgotten to update the version page on the FreeGuide web site for 0.10.5, so users weren&#x27;t notified of that release. I&#x27;ll do it for 0.10.6, and hopefully get some stats together to find out how many people are using it. Now that the stats are working, we may be starting to get some better information.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, I couldn&#x27;t sleep tonight.&lt;&#x2F;p&gt;
&lt;p&gt;No, I won&#x27;t be very clever tomorrow.&lt;&#x2F;p&gt;
&lt;p&gt;But hey, I was clever tonight - dipping into some of the spaghetti that makes FreeGuide tick these days and not only making a change that works, but also &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.svn.sourceforge.net&#x2F;viewvc&#x2F;freeguide-tv?view=rev&amp;amp;revision=1638&quot;&gt;making things a tiny bit better&lt;&#x2F;a&gt; - _that&#x27;s_ an achievement.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-11-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Of course, calling myself clever was a big mistake: &lt;a href=&quot;http:&#x2F;&#x2F;freeguide-tv.svn.sourceforge.net&#x2F;viewvc&#x2F;freeguide-tv?view=rev&amp;amp;revision=1639&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;freeguide-tv.svn.sourceforge.net&#x2F;viewv...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>duckmaze 0.2</title>
          <pubDate>Sat, 10 Nov 2007 00:23:53 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/11/10/duckmaze-02/</link>
          <guid>https://artificialworlds.net/blog/2007/11/10/duckmaze-02/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/11/10/duckmaze-02/">&lt;p&gt;I&#x27;ve finally released to the world the secret extra &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;duckmaze.sourceforge.net&quot;&gt;duckmaze&lt;&#x2F;a&gt; levels. They&#x27;re contained in the 0.2 release, which is also faster, and doesn&#x27;t freeze time.&lt;&#x2F;p&gt;
&lt;p&gt;(As far as I know.)&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s got 18 levels, and a level editor, which means you can make more.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Do it.&lt;&#x2F;li&gt;
&lt;li&gt;Send them to me.&lt;&#x2F;li&gt;
&lt;li&gt;???&lt;&#x2F;li&gt;
&lt;li&gt;Profit.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>Caution: numbers that feel prime may be divisible by 3</title>
          <pubDate>Thu, 08 Nov 2007 09:08:55 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/11/08/caution-numbers-that-feel-prime-may-be-divisible-by-3/</link>
          <guid>https://artificialworlds.net/blog/2007/11/08/caution-numbers-that-feel-prime-may-be-divisible-by-3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/11/08/caution-numbers-that-feel-prime-may-be-divisible-by-3/">&lt;p&gt;Caution: numbers that feel prime may be divisible by 3. For example, 51 and 57.&lt;&#x2F;p&gt;
&lt;p&gt;Conversely, numbers that feel divisible by 3 may be prime. For example, 61, 89 and 97.&lt;&#x2F;p&gt;
&lt;p&gt;Nominations for least prime-feeling numbers that actually are prime: 89 and 101.&lt;&#x2F;p&gt;
&lt;p&gt;Nominations for best prime number: 2 and 101.&lt;&#x2F;p&gt;
&lt;p&gt;Both have digits that sum to 2.&lt;&#x2F;p&gt;
&lt;p&gt;Yes, I was trying to count sheep last night.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Midge
    &lt;span class=&quot;comment_date&quot;&gt;2007-11-30&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;em&gt;A Prime, primes and half a prime&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I was thinking this morning about 34 (nearly my birthday) and how it feels like it should be prime! It is midway between the two nearest primes tho, and two times a prime.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-12-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Two times a prime does often feel special.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Announcing Record TV</title>
          <pubDate>Mon, 24 Sep 2007 09:08:42 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/09/24/announcing-record-tv/</link>
          <guid>https://artificialworlds.net/blog/2007/09/24/announcing-record-tv/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/09/24/announcing-record-tv/">&lt;p&gt;Last night I uploaded the first public version of my latest project, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;recordtv.sourceforge.net&quot;&gt;Record TV&lt;&#x2F;a&gt;. Record TV is a system for recording TV (on a Linux desktop computer) that is designed to allow lots of different user interfaces all to use the same back end. It is currently only useful for people who are quite familiar with the Linux command line. It essentially has no user interface at all, but the back end stuff works for recording TV.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps more excitingly, I have also managed to get my recorded programmes to play back on my Nintendo Wii, so I can watch them on my TV.&lt;&#x2F;p&gt;
&lt;p&gt;Find out more on the project page linked above. I&#x27;ve released this code very early, in the spirit of &quot;release early, release often,&quot; so expect to hack on it a bit to get it working.&lt;&#x2F;p&gt;
&lt;p&gt;If you think MythTV just goes about things the wrong way, and you&#x27;d like to help do it right, it might be of interest.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s mostly Python, with some PHP and shell scripts.&lt;&#x2F;p&gt;
&lt;p&gt;A job I&#x27;d like to do is to be able to use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; as a UI for selecting programmes to record.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Karel
    &lt;span class=&quot;comment_date&quot;&gt;2007-11-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I am really interested to use your program as &quot;freevo recordserver plugin &quot;. In fact, I use an NSLU2 and freevo recordserver is too heavy. If you have any idea on how to do this, could you please send me and email. It will be great.&lt;br&#x2F;&gt;
I’am going to investigate and if I found something , I ll send you an email&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for your program&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-11-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Karel, I’m afraid I have no idea but let me know how you get on!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10.5</title>
          <pubDate>Wed, 19 Sep 2007 14:17:35 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/09/19/freeguide-0105/</link>
          <guid>https://artificialworlds.net/blog/2007/09/19/freeguide-0105/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/09/19/freeguide-0105/">&lt;p&gt;After over a year, and several U-turns, the next version of FreeGuide is out.&lt;&#x2F;p&gt;
&lt;p&gt;I was dead-set on getting recording functionality into it, and when some students turned up asking to help, I directed them towards doing that. They produced a reasonable framework for how to do it (without any of the dirty details of actually recording stuff), but we ended up throwing it away in favour of the way Alex wanted to do it, which was as an extension of the current reminder code.&lt;&#x2F;p&gt;
&lt;p&gt;This way of doing things never reached a stable state, so the trunk was festering for a long time with broken code in it that lost your settings, and tried to record programmes but started too late, and with loads of other usability bugs that I&#x27;d hesitate to call minor since any one of them could put off someone trying the program for the first time and persuade them never to come back.&lt;&#x2F;p&gt;
&lt;p&gt;In the end, since this just wasn&#x27;t getting fixed, I decided we needed to give up on recording and get back to what we&#x27;re good at, which is being a TV guide. There is, of course, quite a lot of overlap in these two functions, but I just didn&#x27;t have time to think through properly what a recording program should do, and how to move FreeGuide over to a sensible model.&lt;&#x2F;p&gt;
&lt;p&gt;So, I reverted the recording code, and it&#x27;s there in the SVN history if anyone wants to resurrect it.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, the thing that motivated me to do this and actually get a release out was the fact that the US listings provider Zap2It has wound up their service, and it&#x27;s been replaced with a new one, called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.schedulesdirect.org&#x2F;&quot;&gt;Schedules Direct&lt;&#x2F;a&gt;, for which you have to pay money.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully, any US and Canadian users of FreeGuide should be able to download the latest version of FreeGuide (and update to the latest XMLTV if they&#x27;re on Linux) and start viewing their listings as soon as they&#x27;ve paid for them from Schedules DIrect. Do let us know on the mailing list if this isn&#x27;t the case.&lt;&#x2F;p&gt;
&lt;p&gt;So, hopefully the crisis has been averted, and now we can get back to doing things right. I&#x27;ve implemented the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;SVN_Rules&quot;&gt;Benevolent Fascist Dictator&lt;&#x2F;a&gt; rule, and so far, trunk is better than 0.10.5, and we could make a release from it any time. I plan to make one every couple of months, to keep things ticking along.&lt;&#x2F;p&gt;
&lt;p&gt;FreeGuide 0.10.5 contains millions of small features and bug fixes, that I didn&#x27;t manage to keep up with while trunk was broken. The next release will be after a much shorter delay, and will have a much better change log.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide SVN now useable</title>
          <pubDate>Sat, 18 Aug 2007 10:14:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/08/18/freeguide-svn-now-useable/</link>
          <guid>https://artificialworlds.net/blog/2007/08/18/freeguide-svn-now-useable/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/08/18/freeguide-svn-now-useable/">&lt;p&gt;Why not try the latest SVN version of FreeGuide, which contains lots of bug fixes over 0.10.4? There are detailed instructions on how to &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;Build_from_SVN&quot;&gt;Build FreeGuide from SVN&lt;&#x2F;a&gt;, and do ask on the developers&#x27; list if you have problems.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve fixed the horrible bug that deleted all your preferences, and I think the code&#x27;s in pretty good shape, with all the recording code gone, but lots of nice additions put in over the last year.&lt;&#x2F;p&gt;
&lt;p&gt;Try it out, and let us know on the developers&#x27; list how you get on - good or bad.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Heartfelt apologies for the feed URL change</title>
          <pubDate>Wed, 15 Aug 2007 12:20:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/08/15/heartfelt-apologies-for-the-feed-url-change/</link>
          <guid>https://artificialworlds.net/blog/2007/08/15/heartfelt-apologies-for-the-feed-url-change/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/08/15/heartfelt-apologies-for-the-feed-url-change/">&lt;p&gt;Sorry, sorry, sorry, sorry. I decided to change to wordpress&#x27; pretty-URLs mode, and it changed the feed URL, which I was not expecting, and which I think is fundamentally borken.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide - putting recording on hold</title>
          <pubDate>Wed, 15 Aug 2007 11:00:26 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/08/15/freeguide-putting-recording-on-hold/</link>
          <guid>https://artificialworlds.net/blog/2007/08/15/freeguide-putting-recording-on-hold/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/08/15/freeguide-putting-recording-on-hold/">&lt;p&gt;FreeGuide has been stalled for about a year because the basic code for recording programmes has been sitting in SVN, but it contained far too many bugs to be useable, even by the developers. This kind of situation completely takes the wind out of development, since any little bug fixes or features people want to send it are either against very old code, or never get written because when they download the latest code it doesn&#x27;t work.&lt;&#x2F;p&gt;
&lt;p&gt;I am more and more of the opinion that the code in your trunk (or HEAD, in CVS terminology) should ALWAYS by _better_ than the latest release. I.e. it should only contain bug fixes and properly tested new features. Anything else (i.e. code that is still being knocked into shape) should live in a branch. Branches are pain, but not as much pain as your project dying because new developers who try and do the right thing (download the latest code) are confronted by non-working code and move on to something more fun.&lt;&#x2F;p&gt;
&lt;p&gt;So, I&#x27;ve reverted the recording code. If we want to resurrect it, we can branch from just before where I reverted, and then merge from the trunk into that branch any other fixes that apply.&lt;&#x2F;p&gt;
&lt;p&gt;The next release of FreeGuide will be from the trunk, and will contain bug fixes and small new features submitted by a couple of people who have stepped up to do the work. I hope, when my life settles slightly, that I might even be one of them.&lt;&#x2F;p&gt;
&lt;p&gt;New baby resolution: never allow non-completely-working code in trunk. Without new developers, your project will die.&lt;&#x2F;p&gt;
&lt;p&gt;Small hypocritical extra note: I completely broke the code in trunk by reverting the recording code, so now it loses all your settings when you exit. Use with extreme caution. This, of course, completely breaks the above resolution. I will fix it when I can, and in the meantime, Rick is looking into it, so hopefully it will be fixed soon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New Baby arrived safely</title>
          <pubDate>Wed, 15 Aug 2007 10:38:23 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/08/15/new-baby-arrived-safely/</link>
          <guid>https://artificialworlds.net/blog/2007/08/15/new-baby-arrived-safely/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/08/15/new-baby-arrived-safely/">&lt;p&gt;I won&#x27;t waste too much of your time, but here she is:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;08&#x2F;img_2191.jpg&quot; alt=&quot;Carys&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;She&#x27;s excellent, and mum is doing really well. Big brother appears to be a little bothered, but presumably that will pass.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>(Badly wrong) usage statistics for FreeGuide</title>
          <pubDate>Tue, 31 Jul 2007 13:53:12 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/07/31/badly-wrong-usage-statistics-for-freeguide/</link>
          <guid>https://artificialworlds.net/blog/2007/07/31/badly-wrong-usage-statistics-for-freeguide/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/07/31/badly-wrong-usage-statistics-for-freeguide/">&lt;p&gt;A long time ago I added functionality to FreeGuide so people could opt to report their use of FreeGuide to under a username, or anonymously. This all happens as part of the latest version check, and you can turn it off, obviously.&lt;&#x2F;p&gt;
&lt;p&gt;Quite a long time ago, Alex broke this functionality, and I haven&#x27;t yet made a release that fixes it. Stupid, really, because the usage statistics we&#x27;ve got are all-but useless:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;unique_hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;unique_hits.thumbnail.png&quot; alt=&quot;Unique FreeGuide users by month (wrong)&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;anon_hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;anon_hits.thumbnail.png&quot; alt=&quot;Anonymous FreeGuide users by month (wrong)&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;hits.png&quot;&gt;&lt;img src=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;07&#x2F;hits.thumbnail.png&quot; alt=&quot;Raw number of FreeGuide startups by month (wrong)&quot; &#x2F;&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully I&#x27;ll get a release out soon that fixes it, and these graphs will become more sensible.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam’s Blog » Blog Archive » FreeGuide usage stats
    &lt;span class=&quot;comment_date&quot;&gt;2011-10-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] long time ago I mentioned I’d added some (opt-in) usage tracking for FreeGuide. Here are the latest stats. I think they […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Procmail Maildir messages disappearing</title>
          <pubDate>Tue, 26 Jun 2007 08:08:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/06/26/procmail-maildir-messages-disappearing/</link>
          <guid>https://artificialworlds.net/blog/2007/06/26/procmail-maildir-messages-disappearing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/06/26/procmail-maildir-messages-disappearing/">&lt;p&gt;Google-friendly title, no?&lt;&#x2F;p&gt;
&lt;p&gt;Executive summary:&lt;&#x2F;p&gt;
&lt;p&gt;In your ~&#x2F;.procmailrc&lt;&#x2F;p&gt;
&lt;p&gt;MAILDIR=$HOME&#x2F;Maildir - is BAD, whereas&lt;&#x2F;p&gt;
&lt;p&gt;MAILDIR=$HOME&#x2F;Maildir&#x2F; - is good.&lt;&#x2F;p&gt;
&lt;p&gt;The slash at the end appears to tell procmail that this is a Maildir directory, rather than something else, elusive.&lt;&#x2F;p&gt;
&lt;p&gt;For a week my messages were appearing in $HOME&#x2F;Maildir, but they were just in there with names like msg.zFS, instead of going into $HOME&#x2F;Maildir&#x2F;new and having a name like 1282544959.Q22432E173.192.0.54.2:1,T.&lt;&#x2F;p&gt;
&lt;p&gt;This meant they effectively disappeared, and I had a quiet, relaxing week.&lt;&#x2F;p&gt;
&lt;p&gt;Followed by a panicked and expletive-filled hour or so.&lt;&#x2F;p&gt;
&lt;p&gt;When I put the slash back, new messages started appearing in my Inbox again.&lt;&#x2F;p&gt;
&lt;p&gt;I was also able just to move the msg.zFS files into &quot;new&quot;, and they appeared fine, which was nice.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I feel much more comfortable now reality has been restored.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I think you should have:&lt;br&#x2F;&gt;
MAILDIR=$HOME&#x2F;Maildir&lt;br&#x2F;&gt;
and&lt;br&#x2F;&gt;
DEFAULT=${MAILDIR}&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;This specifies where your Maildir &quot;tree&quot; is, and that by default you want to deliver into that directory as a top level Maildir format folder.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’m not going to touch it ever again.  It made me too frightened.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;OK, I admit it, perhaps I’m wrong.  I’ve just checked my &#x2F;etc&#x2F;procmail.&lt;br&#x2F;&gt;
MAILDIR=$HOME&#x2F;Maildir&#x2F;&lt;br&#x2F;&gt;
DEFAULT=$MAILDIR&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Today truly is a special day.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-27&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Right, in order not to be wrong I’ve changed it to this:&lt;&#x2F;p&gt;
&lt;p&gt;MAILDIR=$HOME&#x2F;Maildir&lt;br&#x2F;&gt;
DEFAULT=$MAILDIR&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;and am so far suffering from no adverse affects.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>last.fm in GSSMP</title>
          <pubDate>Thu, 07 Jun 2007 02:38:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/06/07/lastfm-in-gssmp/</link>
          <guid>https://artificialworlds.net/blog/2007/06/07/lastfm-in-gssmp/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/06/07/lastfm-in-gssmp/">&lt;p&gt;Obviously I should have gone to bed, but I&#x27;ve got &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;last.fm&quot;&gt;last.fm&lt;&#x2F;a&gt; support working in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;GSSMP&lt;&#x2F;a&gt;. It&#x27;s in the git repo, and I should get a release out as soon as I&#x27;ve written a little bit of documentation.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>lastfmsubmitd on Ubuntu Dapper</title>
          <pubDate>Thu, 07 Jun 2007 00:31:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/06/07/lastfmsubmitd-on-ubuntu-dapper/</link>
          <guid>https://artificialworlds.net/blog/2007/06/07/lastfmsubmitd-on-ubuntu-dapper/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/06/07/lastfmsubmitd-on-ubuntu-dapper/">&lt;p&gt;So I had been thinking about putting &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;last.fm&quot;&gt;last.fm&lt;&#x2F;a&gt; support into &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;GSSMP&lt;&#x2F;a&gt;, and then I got an email to the mailing list suggesting it, and then I couldn&#x27;t sleep tonight, and, as ever, something that sounded easy has turned into a saga.&lt;&#x2F;p&gt;
&lt;p&gt;I decided to use &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.red-bean.com&#x2F;~decklin&#x2F;software&#x2F;lastfmsubmitd&#x2F;&quot;&gt;lastfmsubmitd&lt;&#x2F;a&gt; to handle communicating with last.fm, since it seems silly to re-implement this in every app, and, after worrying that the web site didn&#x27;t mention any binary packages, I was glad to discover that it has indeed been packaged for Ubuntu Edgy, Feisty and Gutsy.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, since I use an distro version that is *gasp* almost ONE YEAR OLD (Ubuntu Dapper) there were no packages for my version.&lt;&#x2F;p&gt;
&lt;p&gt;So I downloaded the Edgy version and tried to install it, but it complained that my version of python-support was too old.&lt;&#x2F;p&gt;
&lt;p&gt;After quite a bit of faffing I extracted the .deb file using ar (1), and then untarred the file inside there called control.tar.gz and edited the file inside there called control so that it said python-support (&amp;gt;= 0.1) instead of 0.2. Simple, eh?&lt;&#x2F;p&gt;
&lt;p&gt;[Of course, all of this assumes that the packager had no good reason for asking for version 0.2. So far, it looks like that assumption was correct.]&lt;&#x2F;p&gt;
&lt;p&gt;After some careful re-tarring and re-arring (if that&#x27;s a word) I had a new .deb, which you can download if you&#x27;re interested, from here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;files&#x2F;lastfmsubmitd_0.32.1-1-friggedbyandy_all.deb&quot;&gt;lastfmsubmitd_0.32.1-1-friggedbyandy_all.deb&lt;&#x2F;a&gt; which appears to install cleanly (so long as I create a config file &#x2F;etc&#x2F;lastfmsubmitd.conf BEFORE I install...).&lt;&#x2F;p&gt;
&lt;p&gt;That config file, by the way, just looks like this:&lt;&#x2F;p&gt;
&lt;pre&gt;[account]
password = mypassword
user = myusername
&lt;&#x2F;pre&gt;
&lt;p&gt;and is owned by the lastfm user, in the root group, and is not readable by &quot;all&quot;. Note that you can&#x27;t make it owned by the lastfm user until after you&#x27;ve installed the deb, because that user won&#x27;t exist until then.&lt;&#x2F;p&gt;
&lt;p&gt;Are you following so far?&lt;&#x2F;p&gt;
&lt;p&gt;Of course, for some unknown reason that deb only contains the daemon lastfmsubmitd, and not the helper program lastfmsubmit, which allows you to actually submit tracks on the command line, despite the fact that the deb installs the man page for that command.&lt;&#x2F;p&gt;
&lt;p&gt;So, I downloaded the relevant &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;archive.ubuntu.com&#x2F;ubuntu&#x2F;pool&#x2F;universe&#x2F;l&#x2F;lastfmsubmitd&#x2F;lastfmsubmitd_0.32.1.orig.tar.gz&quot;&gt;original source package&lt;&#x2F;a&gt;, and just extracted the file lastfmsubmit and put it in my PATH.&lt;&#x2F;p&gt;
&lt;p&gt;After executing a simple command like this:&lt;&#x2F;p&gt;
&lt;pre&gt;sudo chmod a+w &#x2F;var&#x2F;spool&#x2F;lastfm
&lt;&#x2F;pre&gt;
&lt;p&gt;(obviously) it seems to actually work when I run from the command line like so:&lt;&#x2F;p&gt;
&lt;p&gt;lastfmsubmit --artist &quot;Propellerheads&quot; --album &quot;Decksandrumsandrockandroll&quot; --title &quot;A Number of Microphones&quot; --length &quot;45&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Now on to doing what I actually wanted to do...&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;&#x2F;strong&gt; note also the bug in these packages that prevents cron.daily from running: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bugs.launchpad.net&#x2F;ubuntu&#x2F;+source&#x2F;lastfmsubmitd&#x2F;+bug&#x2F;136717&quot;&gt;Ubuntu 136717&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Obviously, there was a bug</title>
          <pubDate>Fri, 01 Jun 2007 19:29:32 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/06/01/obviously-there-was-a-bug/</link>
          <guid>https://artificialworlds.net/blog/2007/06/01/obviously-there-was-a-bug/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/06/01/obviously-there-was-a-bug/">&lt;p&gt;So I&#x27;ve released &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;GSSMP&lt;&#x2F;a&gt; 1.1, which is guaranteed to contain one fewer bugs, and is relatively unlikely to contain any new ones. Also, thanks to JoÃ£o Pinto, it should build without warnings on AMD64 too!&lt;&#x2F;p&gt;
&lt;p&gt;If anyone wants to make an AMD64 package, I&#x27;d be really happy to put that up on the download page. It should be a simple matter of unzipping the source, cd-ing into the gssmp directory, and running make pkg-all.&lt;&#x2F;p&gt;
&lt;p&gt;Go on, it&#x27;ll be fun...&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;How about just installing a cross-compiler on one of your outdated systems?&lt;&#x2F;p&gt;
&lt;p&gt;Oh wait, you’ve already heard how enjoyable it is.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have occasional fantasies about installing about 10 virtual machines – one for each Debian, Ubuntu, Fedora, SuSe etc. version, and writing a scripting interface to automatically build native packages for each system inside the virtual machines.&lt;&#x2F;p&gt;
&lt;p&gt;Is it normal to have fantasies about such things?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-06-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;No.  I could tell you about normal fantasies, then you could exercise your post editing powers.&lt;&#x2F;p&gt;
&lt;p&gt;What you really want is a cross packager.  Packages are just streams of bytes (on all platforms), so given some source code, and some sort of package specification file you should be able to write a C program to produce a set of packages.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Watching TV: a simpler way</title>
          <pubDate>Wed, 16 May 2007 19:00:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/16/watching-tv-a-simpler-way/</link>
          <guid>https://artificialworlds.net/blog/2007/05/16/watching-tv-a-simpler-way/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/16/watching-tv-a-simpler-way/">&lt;p&gt;MPlayer is a black art. GMPlayer, doubly so.&lt;&#x2F;p&gt;
&lt;p&gt;It turns out this does the job much better than my previous hacks:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash
gmplayer -cache 2048 -zoom &quot;dvb:&#x2F;&#x2F;$1&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s still not perfect for radio over digital TV, though - you get an empty window where the picture should be, and the cache is far too large for radio.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>GSSMP 1.0</title>
          <pubDate>Sat, 12 May 2007 06:32:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/12/gssmp-10/</link>
          <guid>https://artificialworlds.net/blog/2007/05/12/gssmp-10/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/12/gssmp-10/">&lt;p&gt;Get it here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&#x2F;&quot;&gt;http:&#x2F;&#x2F;gssmp.sourceforge.net&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is the first time I&#x27;ve declared one of my projects stable, and given it a 1.0 version number.&lt;&#x2F;p&gt;
&lt;p&gt;Prove me wrong by downloading it and breaking it.&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy.&lt;&#x2F;p&gt;
&lt;p&gt;Now I must dash - I&#x27;ve just got to finish off my other projects.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Choose channel dialog</title>
          <pubDate>Thu, 10 May 2007 03:57:06 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/10/choose-channel-dialog/</link>
          <guid>https://artificialworlds.net/blog/2007/05/10/choose-channel-dialog/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/10/choose-channel-dialog/">&lt;p&gt;Following on from the last post about how I got my TV card working, here is a little script I wrote that allows me to choose a channel and start watching. It uses zenity for a rudimentary GUI:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat bin&#x2F;tv_choose_channel
#!&#x2F;bin&#x2F;bash
awk -F &#x27;:&#x27; &#x27;{print $1}&#x27; &amp;lt; .mplayer&#x2F;channels.conf  | \
    zenity --list --title &quot;Choose Channel&quot; \
    --text &quot;Choose a TV channel to watch...&quot; \
    --column &quot;Channels&quot; | xargs -IREP tv_watch &quot;REP&quot;&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Digital TV on Dapper with my Hauppauge WinTV Nova-T card</title>
          <pubDate>Thu, 10 May 2007 03:44:18 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/10/digital-tv-on-dapper-with-my-hauppauge-wintv-nova-t-card/</link>
          <guid>https://artificialworlds.net/blog/2007/05/10/digital-tv-on-dapper-with-my-hauppauge-wintv-nova-t-card/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/10/digital-tv-on-dapper-with-my-hauppauge-wintv-nova-t-card/">&lt;p&gt;My card reports itself like this:&lt;&#x2F;p&gt;
&lt;pre&gt;$ lspci
*snip*
0000:00:0a.0 Multimedia controller: Philips Semiconductors SAA7146 (rev 01)
*snip&lt;&#x2F;pre&gt;
&lt;p&gt;Everything seemed to work out of the box:&lt;&#x2F;p&gt;
&lt;pre&gt;$ dmesg | grep DVB -A 0 -B 3
[17179585.872000] saa7146: register extension &#x27;budget dvb&#x27;.
[17179585.872000] ACPI: PCI Interrupt 0000:00:0a.0[A] -&amp;gt; Link [LNKC]
    -&amp;gt; GSI 11 (level, low) -&amp;gt; IRQ 11
[17179585.872000] saa7146: found saa7146 @ mem e0aa2000
    (revision 1, irq 11) (0x13c2,0x1005).
[17179585.872000] DVB: registering new adapter (TT-Budget&#x2F;WinTV-NOVA-T  PCI).
[17179585.908000] adapter has MAC addr = 00:d0:5c:20:33:92
[17179585.908000] DVB: registering frontend 0 (LSI L64781 DVB-T)...&lt;&#x2F;pre&gt;
&lt;p&gt;So, many, many hours of Googling later, and after many blind alleys and red herrings, I remembered (yes, I have indeed been through all of this before...) what I needed to do next:&lt;&#x2F;p&gt;
&lt;pre&gt;scan &#x2F;usr&#x2F;share&#x2F;doc&#x2F;dvb-utils&#x2F;examples&#x2F;scan&#x2F;dvb-t&#x2F;uk-CrystalPalace &amp;gt; \
    ~&#x2F;.mplayer&#x2F;channels.conf&lt;&#x2F;pre&gt;
&lt;p&gt;and, finally, to watch TV I just do this:&lt;&#x2F;p&gt;
&lt;pre&gt;mplayer -zoom &quot;dvb:&#x2F;&#x2F;BBC ONE&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;That seems to fail sometimes, and this is more reliable:&lt;&#x2F;p&gt;
&lt;pre&gt;$ cat bin&#x2F;tv_watch
#!&#x2F;bin&#x2F;bash
TMPFILE=~&#x2F;Desktop&#x2F;tv.mpg
mplayer -dumpstream -dumpfile &quot;$TMPFILE&quot; &quot;dvb:&#x2F;&#x2F;$1&quot;  &amp;amp;
sleep 5
mplayer -zoom &quot;$TMPFILE&quot;
kill %1
rm -f &quot;$TMPFILE&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;which I run by simply typing:&lt;&#x2F;p&gt;
&lt;pre&gt;tv_watch &quot;BBC FOUR&quot;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Update: the &quot;scan&quot; command is provided by the &quot;dvb-utils&quot; package on Ubuntu.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;What about:&lt;br&#x2F;&gt;
#!&#x2F;bin&#x2F;bash&lt;br&#x2F;&gt;
TMPFILE=~&#x2F;Desktop&#x2F;tvfifo&lt;br&#x2F;&gt;
mkfifo $TMPFILE&lt;br&#x2F;&gt;
mplayer -dumpstream -dumpfile &quot;$TMPFILE&quot; &quot;dvb:&#x2F;&#x2F;$1&quot;  &amp;amp;&lt;br&#x2F;&gt;
sleep 5&lt;br&#x2F;&gt;
mplayer -zoom &quot;$TMPFILE&quot;&lt;br&#x2F;&gt;
kill %1&lt;br&#x2F;&gt;
rm -f &quot;$TMPFILE&quot;&lt;br&#x2F;&gt;
Might be lighter on disc usage.  No particular reason to delete and recreate the fifo every time, though.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-14&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’ll try it tonight.  If it works, you rule.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;It works except gmplayer complains &quot;Seek failed&quot; as it starts.  If I use mplayer instead it’s fine but for radio channels that gives me no window to close when I want the noise to stop.&lt;&#x2F;p&gt;
&lt;p&gt;Why is gmplayer so annoying?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Charles Bailey
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;You only tried gmplayer to take the shine of my ‘rulage’.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Some Googling suggests the &quot;-cache&quot; option.  I’ll try that tonight.   Seems like the best way to do it if gmplayer co-operates.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Restoring a backup with Duplicity</title>
          <pubDate>Wed, 02 May 2007 23:38:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/02/restoring-a-backup-with-duplicity/</link>
          <guid>https://artificialworlds.net/blog/2007/05/02/restoring-a-backup-with-duplicity/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/02/restoring-a-backup-with-duplicity/">&lt;p&gt;See the last post for how I back up my important files to a remote server. To restore them I do this:&lt;&#x2F;p&gt;
&lt;pre&gt;duplicity --file-to-restore path&#x2F;to&#x2F;file.txt
    scp:&#x2F;&#x2F;user@example.com&#x2F;&#x2F;home&#x2F;example&#x2F;bkp&#x2F;home&#x2F;andy&#x2F;Maildir file.txt&lt;&#x2F;pre&gt;
&lt;p&gt;and enter the passphrase when prompted.&lt;&#x2F;p&gt;
&lt;p&gt;Note that I can restore this backup even if I lose my entire home hard drive because it doesn&#x27;t rely on any key file - just my passphrase. This is less secure than using a private key file, but where do you back up your key file to? You know it will be lost when you need it ... safer just to write down your passphrase somewhere, I reckon.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Remote encrypted backup with duplicity</title>
          <pubDate>Wed, 02 May 2007 06:55:42 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/05/02/remote-backup-with-duplicity/</link>
          <guid>https://artificialworlds.net/blog/2007/05/02/remote-backup-with-duplicity/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/05/02/remote-backup-with-duplicity/">&lt;p&gt;My father-in-law&#x27;s hard drive died the other day and I was just able to recover the data using &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.knoppix.org&#x2F;&quot;&gt;Knoppix&lt;&#x2F;a&gt; and my &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.iriver.eu.com&#x2F;&quot;&gt;iRiver&lt;&#x2F;a&gt;. Since I recently discovered a superb program that lets me create encrypted backups on a remote server over ssh, I thought I&#x27;d spread the word. The program is called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;duplicity.nongnu.org&#x2F;&quot;&gt;Duplicity&lt;&#x2F;a&gt; and it is simplicity itself to use. Here is my remote backup script:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash&lt;br&gt;
# Copy this to &#x2F;etc&#x2F;cron.weekly and make it executable and owned by root

# Back up my most important stuff from the local machine to a remote
# server, encrypted with a passphrase

function run_duplicity
{
    echo Backing up &#x27;$1&#x27; ...
    PASSPHRASE=`cat &#x2F;root&#x2F;.blah&#x2F;passphrase` duplicity $1 \
        scp:&#x2F;&#x2F;user@example.com&#x2F;&#x2F;home&#x2F;example&#x2F;bkp$1
}

run_duplicity &#x2F;var&#x2F;lib&#x2F;mysql&#x2F;dbtobackup
run_duplicity &#x2F;home&#x2F;andy&#x2F;Maildir
&lt;&#x2F;pre&gt;
&lt;p&gt;I have one &quot;run_duplicity&quot; line for each directory I want to back up securely. I have my passphrase in a file only reeadable by root in &#x2F;root&#x2F;.blah&#x2F;passphrase, and you either have to type your ssh password each time you run the script, or you can set up a key to let you log in automatically using ssh-keygen.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>GSSMP goes beta</title>
          <pubDate>Tue, 20 Mar 2007 10:10:20 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/03/20/gssmp-goes-beta/</link>
          <guid>https://artificialworlds.net/blog/2007/03/20/gssmp-goes-beta/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/03/20/gssmp-goes-beta/">&lt;p&gt;I&#x27;ve just released version 0.9 of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;GSSMP&lt;&#x2F;a&gt;, and since I&#x27;ve fixed all the bugs I know about (except working around a gstreamer bug, which I will try to do before 1.0), I&#x27;ve marked this release as beta.&lt;&#x2F;p&gt;
&lt;p&gt;This is the second project I&#x27;ve marked as beta, and I am determined I am going to get to 1.0 this time. That means no more features, just bug fixes, and hopefully a 1.0 release in a few weeks, when it&#x27;s had a bit of testing.&lt;&#x2F;p&gt;
&lt;p&gt;Really, I think FreeGuide should have had a 1.0 release a couple of years ago, but I&#x27;ve always had a lot of features in mind before I feel it is complete. I think this was a mistake: it was pretty stable at one point, and it might have been helpful to label it as such.&lt;&#x2F;p&gt;
&lt;p&gt;GSSMP is not going to fall into that trap! It&#x27;s supposed to be minimal, and that is what it is.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s surprising how many features would be nice to have in a minimal app, though, like being a drag and drop target and checking the MIME types of files before playing them, not to mention cool stuff like gapless playback and volumne balancing...&lt;&#x2F;p&gt;
&lt;p&gt;But no, these will have to come after 1.0. My focus needs to go back onto FreeGuide after GSSMP 1.0, as I&#x27;ve been neglecting it, and it needs some love if it&#x27;s ever going to get to 0.11 ... and 0.12 ... and so on for infinity ... and then after that, 1.0.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Recording in FreeGuide</title>
          <pubDate>Wed, 14 Mar 2007 12:55:22 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/03/14/recording-in-freeguide/</link>
          <guid>https://artificialworlds.net/blog/2007/03/14/recording-in-freeguide/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/03/14/recording-in-freeguide/">&lt;p&gt;If you download the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;Run_the_nightly_build&quot;&gt;nightly builds&lt;&#x2F;a&gt; of FreeGuide, you can try out the new recording functionality.&lt;&#x2F;p&gt;
&lt;p&gt;There is quite a bit to do before this is ready for release. There are several bugs in the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;TODO_list&quot;&gt;TODO list&lt;&#x2F;a&gt; and you have to create your own scripts at the moment to launch mplayer or whichever recording software you want to use.&lt;&#x2F;p&gt;
&lt;p&gt;When the release happens, we will incude scripts that are useful for common TV cards (actually, it&#x27;s beginning to look like mplayer will handle all this for us, so all we need is one script) and make it easy to set up, with a default setup that should work for most people out of the box. Or that&#x27;s the idea.&lt;&#x2F;p&gt;
&lt;p&gt;The good news is that Alex appears to be back and working on fixing the bugs, so the ever-receding horizon of the next release is somewhat less receding.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve not quite managed to get it to actually record something yet, (mainly due to mplayer being extremely awkward) but it shoud work...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My daily backup</title>
          <pubDate>Fri, 23 Feb 2007 06:26:37 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/02/22/my-daily-backup/</link>
          <guid>https://artificialworlds.net/blog/2007/02/22/my-daily-backup/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/02/22/my-daily-backup/">&lt;p&gt;I recently recovered my files from my daily backup, which had actually worked!&lt;&#x2F;p&gt;
&lt;p&gt;(Pop quiz: this is how I deleted half my home dir - what is wrong with this Makefile?&lt;&#x2F;p&gt;
&lt;pre&gt;TMP_DIR=&#x2F;tmp&#x2F;gssmp

blah: blah1
    blah

...

blah7:
    ...
    rm -rf $TMPDIR&#x2F;

...

&lt;&#x2F;pre&gt;)
&lt;p&gt;Anyway, I am extremely grateful that the backup did work, but a slight problem was that I have been backing up absolutely everything, and never deleting anything that was backed up until I ran out of disk space.&lt;&#x2F;p&gt;
&lt;p&gt;So when I restored from backup, every file I had ever deleted (since I started backing up) re-appeared. This included files and dirs (and email) that had just been moved somewhere else - I now had 2 copies of them.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve spent the night deleting stuff that I didn&#x27;t actually want restored, and this has encouraged me to create an improved backup script that puts deleted stuff into a separate folder, named after the day it was deleted. I can delete those folders at my leisure when they get too big, but when a disaster happens like it did on Wednesday, I can restore back to a sane state by just ignoring any irrelevant deleted folders.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s quite simple, but I hadn&#x27;t thought of it before, so it might be helpful to others. Enjoy:&lt;&#x2F;p&gt;
&lt;pre&gt;#!&#x2F;bin&#x2F;bash

# Copy this to &#x2F;etc&#x2F;cron.daily and make it owned by root, and executable

# Back up everything important from my main drive (hda) to the backup
# drive (hdb).

# Keep stuff that has been deleted from the real drive in a separate
# backup dir, named after the day it was deleted.

BKP_DRIVE=&#x2F;media&#x2F;hdb1

FLAGS=&quot;-a --delete-during --max-delete 100 --backup&quot;

DT=`date +%F`
DELETED_DIR=deleted-$DT

function run_rsync
{
    THIS_DIR=$1
    EXCLUDES=$2

    echo rsync $FLAGS --backup-dir=$BKP_DRIVE&#x2F;$DELETED_DIR$THIS_DIR \
        $EXCLUDES $THIS_DIR&#x2F; $BKP_DRIVE$THIS_DIR&#x2F;
    rsync $FLAGS --backup-dir=$BKP_DRIVE&#x2F;$DELETED_DIR$THIS_DIR \
        $EXCLUDES $THIS_DIR&#x2F; $BKP_DRIVE$THIS_DIR&#x2F;
}

run_rsync &#x2F;home&#x2F;andy \
    &quot;--exclude &#x2F;iRiver --exclude &#x2F;downloads --exclude &#x2F;.Trash --exclude &#x2F;misc&#x2F;qemu&quot;

run_rsync &#x2F;etc

run_rsync &#x2F;var

run_rsync &#x2F;boot
&lt;&#x2F;pre&gt;
</description>
      </item>
      <item>
          <title>Creating .deb and RPM packages</title>
          <pubDate>Fri, 23 Feb 2007 05:37:25 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/02/22/creating-deb-and-rpm-packages/</link>
          <guid>https://artificialworlds.net/blog/2007/02/22/creating-deb-and-rpm-packages/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/02/22/creating-deb-and-rpm-packages/">&lt;p&gt;Since I&#x27;ve managed to get it to work for GSSMP, I thought I might share how I am generating .deb and RPM packages using a Makefile. These make it much easier for Ubuntu and Fedora Linux users (respectively) to install the program. It should also be helpful for people using Debian, who can try the .deb, and other RPM-based distros, who can try the RPM.&lt;&#x2F;p&gt;
&lt;p&gt;This project is all &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnu.org&#x2F;software&#x2F;autoconf&#x2F;&quot;&gt;autotool&lt;&#x2F;a&gt;ed-up, so this may not be very helpful if you don&#x27;t use autotools.&lt;&#x2F;p&gt;
&lt;p&gt;(Side tip: I don&#x27;t understand autotools at all, but since I created the project with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;glade.gnome.org&#x2F;&quot;&gt;glade&lt;&#x2F;a&gt;, it did all the hard work for me.)&lt;&#x2F;p&gt;
&lt;p&gt;I created these files for the Ubuntu .deb package:&lt;&#x2F;p&gt;
&lt;p&gt;changelog:&lt;&#x2F;p&gt;
&lt;pre&gt;gssmp (0.8-0ubuntu1) edgy; urgency=low

  * First release packaged for ubuntu

 -- Andy Balaam &amp;lt;myemailaddress&amp;gt;  Thu, 15 Feb 2007 12:33:11 +0000&lt;&#x2F;pre&gt;
&lt;p&gt;copyright:&lt;&#x2F;p&gt;
&lt;pre&gt;This package was debianized by Andy Balaam &amp;lt;myemailaddress&amp;gt; on
Thu, 15 Feb 2007 12:33:11 +0000.

It was downloaded from
http:&#x2F;&#x2F;sourceforge.net&#x2F;project&#x2F;showfiles.php?group_id=187144&amp;amp;package_id=219138

Copyright Holder: Andy Balaam &amp;lt;myemailaddress&amp;gt;

License:
   This package is free software; you can redistribute it and&#x2F;or modify
   blah blah blah GPL boilerplate&lt;&#x2F;pre&gt;
&lt;p&gt;control:&lt;&#x2F;p&gt;
&lt;pre&gt;Source: gssmp
Section: sound
Priority: optional
Maintainer: Andy Balaam &amp;lt;myemailaddress&amp;gt;
Build-Depends: debhelper (&amp;gt;= 4.0.0), autotools-dev, libgstreamer0.10-dev, libgnomeui-dev
Standards-Version: 3.6.2

Package: gssmp
Architecture: any
Depends: libgstreamer0.10-0, libgnomeui-0
Description: Gnome Simple Stateful Music Player
 GSSMP is a music player designed to work with Gnome.  It does not store a
 database of all your music, but is designed to work with music files organised
 into directories.  It is small, unobtrusive, and tries to do what you would
 expect, instead of asking you questions.
 .
 It remembers which file was playing on exit, and continues that file from the
 same place when you start again.  It remembers tracks you have been listening
 to recently and displays them in the &quot;Recent&quot; menu.&lt;&#x2F;pre&gt;
&lt;p&gt;I put those three files in my source tree under install&#x2F;deb&#x2F;.&lt;&#x2F;p&gt;
&lt;p&gt;For the RPM, I just needed one file, gssmp.spec:&lt;&#x2F;p&gt;
&lt;pre&gt;###
### RPM spec file for Gnome Simple Stateful Music Player
###
### Adapted from jEdit&#x27;s spec file http:&#x2F;&#x2F;www.jedit.org
###
Summary: A music player that doesn&#x27;t interfere.
Name: gssmp
Provides: gssmp
Version: 0.8
Release: 1
License: GPL
Group: Applications&#x2F;Multimedia&#x2F;
Source0: %{name}-%{version}.tar.bz2
URL: http:&#x2F;&#x2F;gssmp.sourceforge.net&#x2F;
Vendor: Andy Balaam &amp;lt;myemailaddress&amp;gt;
Packager: Andy Balaam &amp;lt;myemailaddress&amp;gt;
BuildArch: i386
BuildRoot: %{_builddir}&#x2F;%{name}-root
Requires: libgnomeui, gstreamer
BuildRequires: libgnomeui-devel, gstreamer-devel

%description
GSSMP is a music player designed to work with Gnome.  It does not store a
database of all your music, but is designed to work with music files organised
into directories.  It is small, unobtrusive, and tries to do what you would
expect, instead of asking you questions.

It remembers which file was playing on exit, and continues that file from the
same place when you start again.  It remembers tracks you have been listening
to recently and displays them in the &quot;Recent&quot; menu.

%prep
%setup -n %{name}-%{version}

%build
.&#x2F;configure --prefix=&#x2F;usr
make

%install
make DESTDIR=%{buildroot} install

%clean
rm -rf %{buildroot}

%files
&#x2F;usr&#x2F;bin&#x2F;gssmp
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;AUTHORS
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;COPYING
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;INSTALL
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;NEWS
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;README
&#x2F;usr&#x2F;share&#x2F;doc&#x2F;gssmp&#x2F;TODO&lt;&#x2F;pre&gt;
&lt;p&gt;which I put in install&#x2F;rpm&lt;&#x2F;p&gt;
&lt;p&gt;Then I modified my Makefile.am to build the deb and RPM when I told it to:&lt;&#x2F;p&gt;
&lt;pre&gt;APP_NAME=gssmp

# Create a deb package
pkg-deb: pkg-src
        - rm -r $(TMP_DIR)
        mkdir -p $(TMP_DIR)
        cp pkg&#x2F;$(APP_NAME)-$(VERSION).tar.bz2 $(TMP_DIR)&#x2F;
        tar --directory $(TMP_DIR)&#x2F; \
                -xjf $(TMP_DIR)&#x2F;$(APP_NAME)-$(VERSION).tar.bz2
        cd $(TMP_DIR)&#x2F;$(APP_NAME)-$(VERSION)&#x2F;; \
                echo | dh_make --single --copyright gpl -e myemailaddress -f \
                        ..&#x2F;$(APP_NAME)-$(VERSION).tar.bz2
        cp install&#x2F;deb&#x2F;changelog install&#x2F;deb&#x2F;control \
                install&#x2F;deb&#x2F;copyright $(TMP_DIR)&#x2F;$(APP_NAME)-$(VERSION)&#x2F;debian&#x2F;
        cd $(TMP_DIR)&#x2F;$(APP_NAME)-$(VERSION)&#x2F;; \
                rm debian&#x2F;README.Debian debian&#x2F;*.ex debian&#x2F;*.EX; \
                .&#x2F;configure; \
                dpkg-buildpackage -rfakeroot; \
                mv ..&#x2F;*.deb $(PWD)&#x2F;pkg&#x2F;
        rm -r $(TMP_DIR);

# Create an RPM package
pkg-rpm: pkg-src
        mkdir -p $(HOME)&#x2F;.rpm&#x2F;RPMS&#x2F;i386
        mkdir -p $(HOME)&#x2F;.rpm&#x2F;SRPMS
        mkdir -p $(HOME)&#x2F;.rpm&#x2F;BUILD
        mkdir -p $(HOME)&#x2F;.rpm&#x2F;SOURCES
        mkdir -p $(HOME)&#x2F;.rpm&#x2F;tmp
        - rm -r $(HOME)&#x2F;.rpm&#x2F;BUILD&#x2F;$(APP_NAME)-root
        - rm -r $(HOME)&#x2F;.rpm&#x2F;RPMS&#x2F;$(APP_NAME)-*
        - rm -r $(HOME)&#x2F;.rpm&#x2F;SRPMS&#x2F;$(APP_NAME)-*
        - rm -r $(HOME)&#x2F;.rpm&#x2F;SOURCES&#x2F;$(APP_NAME)-*
        cp pkg&#x2F;$(APP_NAME)-$(VERSION).tar.bz2 $(HOME)&#x2F;.rpm&#x2F;SOURCES&#x2F;
        rpmbuild --quiet --nodeps --define=&quot;_topdir $(HOME)&#x2F;.rpm&quot; \
            -ba install&#x2F;rpm&#x2F;gssmp.spec
        mv $(HOME)&#x2F;.rpm&#x2F;RPMS&#x2F;i386&#x2F;$(APP_NAME)-$(VERSION)-*.i386.rpm pkg&#x2F;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I just needed to run .&#x2F;autogen.sh to regenerate my Makefile, and then make pkg-deb or make pkg-rpm and the deb or RPM appears magically in the pkg&#x2F; directory.&lt;&#x2F;p&gt;
&lt;p&gt;Autotools, for all its faults, does make this stuff relatively easy. All the code for installing in different places (installing at all) and general flexibility is done for you. Of course, it&#x27;s at the expense of ever understanding what&#x27;s going on, but you can&#x27;t have everything.&lt;&#x2F;p&gt;
&lt;p&gt;Now I just need to make a man page ... and fix all the bugs ... oh, and make an icon ... and make it accept a filename on the command line ... and sort out MIME types ... etc ... so it should be finished really soon.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    myITcorner: Tomasz Gebarowski programming blog » Blog Archive » Making Debian library package with dh_make
    &lt;span class=&quot;comment_date&quot;&gt;2008-10-12&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;[…] Debian New Maintainers’ Guide  Andy Balaam’s Blog Tags: debian, gnu, library, […]&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Moved to WordPress</title>
          <pubDate>Fri, 09 Feb 2007 14:16:09 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/02/09/hello-world/</link>
          <guid>https://artificialworlds.net/blog/2007/02/09/hello-world/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/02/09/hello-world/">&lt;p&gt;I&#x27;ve moved my blog to WordPress, which will allows you to comment on my posts.&lt;&#x2F;p&gt;
&lt;p&gt;I guess it&#x27;s a sad day for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;diyblog&quot;&gt;DIYBlog&lt;&#x2F;a&gt;, but hey, it was just a workaround until I got some web space that supported PHP.&lt;&#x2F;p&gt;
&lt;p&gt;Happy interaction!&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Matt
    &lt;span class=&quot;comment_date&quot;&gt;2007-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Please fix &lt;a href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;diyblog&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;diyblog&#x2F;&lt;&#x2F;a&gt; so that it no longer says &quot;To see what you can make with DIYBlog, see my blog.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I was interested in seeing what DIYblog looks like so I loaded your blog and said to myself: &quot;wow, his DIYblog skin looks just like WordPress!&quot;  Only then I scrolled to the bottom and realised that it wasn’t DIYblog afterall!&lt;&#x2F;p&gt;
&lt;p&gt;If you still intend to keep your DIYblog page then it would be nice for that &quot;my blog&quot; link to go to an archive of your old blog so people can see what kind of output DIYblog produces.  If you haven’t saved an old copy of your blog then you could use one of the copies from the Wayback Machine:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20060521070058&#x2F;http:&#x2F;&#x2F;www.balaam.eclipse.co.uk&#x2F;artificialworlds&#x2F;blog&#x2F;&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20060521070058&#x2F;htt...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-03-28&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Matt, sorry about that!&lt;&#x2F;p&gt;
&lt;p&gt;I’ve modified the page to point at my personal blog aggregation page, which is created using diyblog.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>Two new babies</title>
          <pubDate>Wed, 17 Jan 2007 23:11:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2007/01/17/two-new-babies/</link>
          <guid>https://artificialworlds.net/blog/2007/01/17/two-new-babies/</guid>
          <description xml:base="https://artificialworlds.net/blog/2007/01/17/two-new-babies/">&lt;p&gt;I have 2 new babies to tell you about:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gssmp.sourceforge.net&quot;&gt;Gnome Simple Stateful Music Player&lt;&#x2F;a&gt; which is a music player that just stays out of your face, playing music off your hard drive without making a database or anything like that. It uses &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnome.org&#x2F;&quot;&gt;GNOME&lt;&#x2F;a&gt;&#x2F;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gtk.org&quot;&gt;GTK&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.gnome.org&#x2F;projects&#x2F;gconf&quot;&gt;GConf&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gstreamer.freedesktop.org&quot;&gt;gstreamer&lt;&#x2F;a&gt; and is written in C, which is great fun.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I think C and Python are actually very similar: they are direct languages that are designed to be simple, and allow you to specify what you want to happen in a very straightforward way.&lt;&#x2F;p&gt;
&lt;p&gt;GSSMP is nearly ready for a 0.1 release - it&#x27;s simple, but it does most of what I want. Its killer feature is that it remembers what you were listening to and continues from there when you restart. Sounds simple, I know, but sorely lacking from some music programs. It also starts quickly, as opposed to, say, slowly or, for example, very slowly.&lt;&#x2F;p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Another one:&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;69.89.27.216&#x2F;%7Eartific2&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;02&#x2F;scan-2007-01-17-arrows-small.png&quot; alt=&quot;Scan&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New games!</title>
          <pubDate>Thu, 21 Dec 2006 17:48:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/12/21/new-games/</link>
          <guid>https://artificialworlds.net/blog/2006/12/21/new-games/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/12/21/new-games/">&lt;p&gt;I&#x27;ve been really enjoying the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;gp2x.co.uk&quot;&gt;GP2X&lt;&#x2F;a&gt; I got for our 5-year anniversary (!) and have got DVD ripping working, along with Quake playing at a decent speed, and lots of downloadable games, notably &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;archive.gp2x.de&#x2F;cgi-bin&#x2F;cfiles.cgi?0,0,0,0,30,930&quot;&gt;Spout&lt;&#x2F;a&gt;, which is excellent, and (unlike many games which are really too visually complex to see on a small screen) simple enough to work on a handheld. There are a few other good games too, and lots that I immediately deleted off my SD card. I&#x27;d like to see some of the very polished &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;lgames.sourceforge.net&quot;&gt;LGames&lt;&#x2F;a&gt; ported - maybe I&#x27;ll get around to it myself sometime.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, all of that is peripheral really, because what I really like doing is writing games, not playing them. So far I have imposed two games on the unsuspecting public:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;mopesnake.sourceforge.net&quot;&gt;mop(e)snake&lt;&#x2F;a&gt; - a classic snake game with no featuritis - just a snake, apples (pain, actually - read the plot if you&#x27;re interested...) and you. Try to &lt;em&gt;&quot;fill the screen with green&quot;&lt;sup&gt;TM&lt;&#x2F;sup&gt;&lt;&#x2F;em&gt;. It&#x27;s reasonably mature now and works on GP2X, Windows and Linux. There&#x27;s not a lot left to do, except a bit of polish and fixing any bugs that come up.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;duckmaze.sourceforge.net&quot;&gt;duckmaze&lt;&#x2F;a&gt; - a maze game where you can move walls. This is based on a game I played years ago on a handheld, whose name I have forgotten. It&#x27;s potentially quite fun, but I need to design some more levels - so far it only has 8, and several of them are pretty noddy. The first release includes a level editor (go me), so you can help out with designing more. Actually, you don&#x27;t even really need the level editor, since the level file format is designed to be fairly human-readable, so potentially you could create levels in a text editor. I&#x27;m quite pleased with that, even though no-one is ever going to appreciate it now I&#x27;ve written the level editor.&lt;&#x2F;p&gt;
&lt;p&gt;Download, enjoy, write to the mailing lists with ideas, bugs, code etc.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Linux Journal - Forgiveness</title>
          <pubDate>Mon, 18 Dec 2006 22:15:33 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/12/18/linux-journal-forgiveness/</link>
          <guid>https://artificialworlds.net/blog/2006/12/18/linux-journal-forgiveness/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/12/18/linux-journal-forgiveness/">&lt;p&gt;Dear LJ,&lt;&#x2F;p&gt;
&lt;p&gt;A few months ago I wrote to you saying we had to break up - you had become too chatty and opinion-filled for my taste.&lt;&#x2F;p&gt;
&lt;p&gt;I have to admit that it turns out I was bluffing.&lt;&#x2F;p&gt;
&lt;p&gt;I love you too much, and today I renewed my subscription.&lt;&#x2F;p&gt;
&lt;p&gt;You did take a significant dip in quality, but things have got better over the last couple of months.&lt;&#x2F;p&gt;
&lt;p&gt;More important, though, is that fact that you started from such a high quality base that even when it dipped, you were still the only Linux publication for me.&lt;&#x2F;p&gt;
&lt;p&gt;Yours forever,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Another FreeGuide release candidate</title>
          <pubDate>Wed, 19 Jul 2006 09:16:48 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/07/19/another-freeguide-release-candidate/</link>
          <guid>https://artificialworlds.net/blog/2006/07/19/another-freeguide-release-candidate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/07/19/another-freeguide-release-candidate/">&lt;p&gt;I screwed up the RPM, so if you tried that, try the latest one from here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;rc&quot;&gt;freeguide-tv.sf.net&#x2F;rc&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10.4 release candidate</title>
          <pubDate>Tue, 04 Jul 2006 16:47:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/07/04/freeguide-0104-release-candidate/</link>
          <guid>https://artificialworlds.net/blog/2006/07/04/freeguide-0104-release-candidate/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/07/04/freeguide-0104-release-candidate/">&lt;p&gt;Lots and lots of bug fixes, and some cool features sneaked in by people who obviously didn&#x27;t read my TODO list ;) means that FreeGuide 0.10.4 is nearly ready.&lt;&#x2F;p&gt;
&lt;p&gt;You can test it by downloading the relevant installer from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;rc&quot;&gt;freeguide-tv.sf.net&#x2F;rc&lt;&#x2F;a&gt;. I&#x27;ve put up a Linux RPM and tarball, and hopefully a Windows installer and other OS&#x2F;distro packages will be up soon.&lt;&#x2F;p&gt;
&lt;p&gt;Reasons to try it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;There are loads of bug fixes in it.&lt;&#x2F;li&gt;
&lt;li&gt;You can now search for programmes!&lt;&#x2F;li&gt;
&lt;li&gt;You can try the new vertical list view.&lt;&#x2F;li&gt;
&lt;li&gt;It will help us if we find bugs before the release, and that will make you feel good.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>A Quick and desperate introduction to data recovery</title>
          <pubDate>Thu, 08 Jun 2006 04:07:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/06/07/a-quick-and-desperate-introduction-to-data-recovery/</link>
          <guid>https://artificialworlds.net/blog/2006/06/07/a-quick-and-desperate-introduction-to-data-recovery/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/06/07/a-quick-and-desperate-introduction-to-data-recovery/">&lt;p&gt;No, I did not just develop a passing interest in data recovery as a leisure activity. Presumably like everyone else, I learnt about it because it was necessary.&lt;&#x2F;p&gt;
&lt;p&gt;A year or two ago my friend&#x27;s computer was packing up, and he asked me to extract and store all their files for him until he got hold of a new one. These files were basically the whole history of their life in photo, movie, word document etc. form - it totalled about 3.9GB. We were pretty successful about extracting the files from his dying machine by booting into Linux and copying them onto my iRiver. I&#x27;ve been keeping hold of them ever since, with a copy on my iRiver and another on my home machine to be sure.&lt;&#x2F;p&gt;
&lt;p&gt;During the fairly disastrous upgrade of my machine from Fedora Core 3 to Ubuntu Dapper Drake that I did last week (of which, probably, more later) I managed to delete all the little scripts I keep in my home directory to do useful things. (They represent quite a lot of collected wisdom, and losing them has been annoying, but anyway.) One of those scripts called other scripts to delete old stuff I wasn&#x27;t interested in from my iRiver, and synchronise my local copy of my iRiver&#x27;s contents with the machine itself.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously, I made a small mistake in re-writing that script, which meant that it started deleting everything older than 2 weeks old off my entire home directory. Since I had gone off to bed while this was running, I was extremely lucky that it hit a read-only file fairly soon so I didn&#x27;t lose much, but what I did lose was the zip file into which I had put all my friend&#x27;s files.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, my synchronisation program worked like a dream, running as scheduled that night and merrily deleting the zip from my iRiver too. Lesson 1: &lt;strong&gt;Don&#x27;t let your backup program delete files automatically&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If it had been my own data I would have been gutted, but the thought that I had lost someone else&#x27;s lifetime collections of photos and sentimental things was pretty horrible.&lt;&#x2F;p&gt;
&lt;p&gt;So I began several long nights of investigating data recovery. I assumed it would be easier to get the files off my Linux ext3 partition, since it is well documented and hackable, but in fact, I learned lesson number 2: &lt;strong&gt;You can&#x27;t recover deleted files off an ext3 partition.&lt;&#x2F;strong&gt; That is probably not strictly true, but in practice, it is. The reason is that when you delete a file in ext3 it blanks the record of it from somewhere, so all that&#x27;s left is the actual data with no index entry. This makes it pretty much impossible to recover anything.&lt;&#x2F;p&gt;
&lt;p&gt;I tried lots of different things, including using grep and strings to search the device directly, and debugfs which appears only to be useful for ext2. The closest I got was a program called &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;foremost.sourceforge.net&#x2F;&quot;&gt;foremost&lt;&#x2F;a&gt; which knows what the start and end of each type of file looks like, and searches the raw data for things matching the type of file you are looking for. Foremost seems really cool, and it found lots of things that looked like zip files, but almost none of them actually were valid zips, and it didn&#x27;t find anything big enough to be the file I wanted anyway.&lt;&#x2F;p&gt;
&lt;p&gt;So much for my Linux drive. Lesson 3: &lt;strong&gt;Consider ext2 for a backup partition&lt;&#x2F;strong&gt;. It is apparently easy to undelete deleted files on ext2. On the other hand, if the computer crashes while writing to an ext2 drive, you are much more likely to have corrupted data than if you use ext3. Quelle dommage.&lt;&#x2F;p&gt;
&lt;p&gt;I find it quite pleasing that the fact that I had a backup meant not that my files were backed up, which would of course be too easy for a person who has recently been branded &lt;em&gt;Master of Pain (Receiving)&lt;&#x2F;em&gt; by his colleagues, but it did mean I had another deleted file on a different filesystem which I could attempt to recover.&lt;&#x2F;p&gt;
&lt;p&gt;My iRiver (which, incidentally, I love even more after this incident than I did before) uses a FAT32 filesystem, which everyone agrees is rubbish, but which has the highly relevant advantage of being simple and thus less difficult to recover data from.&lt;&#x2F;p&gt;
&lt;p&gt;I made a copy of the entire disk by doing dd &#x2F;dev&#x2F;sd1 &amp;gt; myhd.raw and then I was free to attempt recovery on this raw file without fear that I would overwrite something on the real disk.&lt;&#x2F;p&gt;
&lt;p&gt;I hoped it would be easy to get the file back off FAT32, but it turned out to be difficult as well, mainly because the file was so huge. I tried lots of different programs, most of which crashed unceremoniously, or couldn&#x27;t find my file. Lesson 4: &lt;strong&gt;Don&#x27;t give up&lt;&#x2F;strong&gt;. Many of these programs were lying to me - telling me to give up. Had it been my own data, I would have given up. We are several days into my nightly trial now. I was tired.&lt;&#x2F;p&gt;
&lt;p&gt;Then, like a shining beacon of professionalism and respect for data, over the horizon came &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sleuthkit.org&#x2F;autopsy&#x2F;&quot;&gt;Autopsy&lt;&#x2F;a&gt; and its underlying &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sleuthkit.org&quot;&gt;Sleuth Kit&lt;&#x2F;a&gt; tools. I installed them, and started the server by typing autopsy and then pointed my web browser at http:&#x2F;&#x2F;localhost:9999 and followed the instructions. It could see my file! I told it to recover it, and my browser said it was downloading the file.&lt;&#x2F;p&gt;
&lt;p&gt;And then it failed. I tried again. It failed.&lt;&#x2F;p&gt;
&lt;p&gt;So I tried with wget. It failed. I tried with curl. It failed.&lt;&#x2F;p&gt;
&lt;p&gt;I realised the problem - the file was over 2GB, which is not supported in some downloading software! I tried with wget again, after confirming that it supported large files. It failed again.&lt;&#x2F;p&gt;
&lt;p&gt;Lesson 5, which really I should have figured out ages ago is: &lt;strong&gt;Don&#x27;t use large files&lt;&#x2F;strong&gt;. Especially not large zip files containing all your precious data, obfuscating the files from your recovery program.&lt;&#x2F;p&gt;
&lt;p&gt;Now, I knew autopsy was running on my local machine and then providing the file I needed as a download, so I knew I could access the file directly by using the tools autopsy was using underneath.&lt;&#x2F;p&gt;
&lt;p&gt;Autopsy, may the Lord bless you for making those tools accessible separately.&lt;&#x2F;p&gt;
&lt;p&gt;Autopsy had told me the inode number of the file I wanted, so a quick read of the icat tool&#x27;s man page showed me what to do. I ran the command and ended up with a 3.9GB file!&lt;&#x2F;p&gt;
&lt;p&gt;So I unzipped it.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously, it failed.&lt;&#x2F;p&gt;
&lt;p&gt;Full of optimism, I assumed that this really was my file, but that it had been corrupted slightly by having some of its data over-written. I started looking for zip file recovery programs.&lt;&#x2F;p&gt;
&lt;p&gt;To cut a long story short, there are a lot of zip file recovery programs for Windows and Linux, and some of the Windows ones appear to work using Wine, but in the end (which I should have thought of from the beginning) the one that actually worked, rather than tantalising you with the names of the files and then either crashing or charging you money to recover them, is the one written by the guy who invented the zip file. The trial version of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.pkware.com&#x2F;home_and_small_office&#x2F;&quot;&gt;PKZip for Windows&lt;&#x2F;a&gt;, running on a Windows machine successfully opened and repaired my damaged archive, with this summary message:&lt;&#x2F;p&gt;
&lt;p&gt;Extracted 3,151 files&lt;&#x2F;p&gt;
&lt;p&gt;Skipped 0 files&lt;&#x2F;p&gt;
&lt;p&gt;62 errors&#x2F;warnings&lt;&#x2F;p&gt;
&lt;p&gt;Yes, out of 3,151 files, 62 were damaged, and the rest were fine.&lt;&#x2F;p&gt;
&lt;p&gt;It was a long journey, but we made it. Finally, lesson 6: &lt;strong&gt;If it&#x27;s important, have 2 backups&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Grant
    &lt;span class=&quot;comment_date&quot;&gt;2008-02-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Brilliant write up, and much sypathy, and congratulations!&lt;&#x2F;p&gt;
&lt;p&gt;The funny thing is, I have messed up more files, when backing up, then if I had left them alone!&lt;&#x2F;p&gt;
&lt;p&gt;I’m currently looking for 6hrs of work I deleted accidently after migrating a machine. Proably will take me more time to recover them!&lt;&#x2F;p&gt;
&lt;p&gt;My mistake was  to use a GUI copying program in linux, not as root! (should have used the CL!) when moving files.&lt;&#x2F;p&gt;
&lt;p&gt;Now I have to try and recover some files from an ext3 partition.  I also foolishly didn’t partition of my data directory, which would have saved me some of the agro.&lt;&#x2F;p&gt;
&lt;p&gt;Wish me luck!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-02-03&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Grant, good luck!&lt;&#x2F;p&gt;
&lt;p&gt;I’m afraid you’re going to need it – ext3 seems to be extremely difficult.&lt;&#x2F;p&gt;
&lt;p&gt;My fingers are crossed for you.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>FreeGuide fan mail</title>
          <pubDate>Thu, 18 May 2006 22:41:44 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/05/18/freeguide-fan-mail/</link>
          <guid>https://artificialworlds.net/blog/2006/05/18/freeguide-fan-mail/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/05/18/freeguide-fan-mail/">&lt;p&gt;Really encouraged to receive this:&lt;&#x2F;p&gt;
&lt;p&gt;Hello Mr Balaam,&lt;&#x2F;p&gt;
&lt;p&gt;A quick email to thank you for giving the world the brilliant Freeguide.
(I saw in your blog that you have a high volume of email - no reply needed).&lt;&#x2F;p&gt;
&lt;p&gt;I tried Digiguide for a while, loved the principle, found the interface too cluttered. Went back to downloading pages from here and there, and dipping into the Radio Times timeline, which is not quite customisable enough.&lt;&#x2F;p&gt;
&lt;p&gt;I happen to live in a place where I get an unusual mix of channels - UK free satellite + UK terrestrial +Irish terrestrial. I&#x27;d never found a single listings service which covers the options.&lt;&#x2F;p&gt;
&lt;p&gt;Then, by chance, I found Freeguide, and I love it. I couldn&#x27;t ask for more (except, perhaps, for customisable colours for program categories :-) ) Incidentally, one of your blog entries wondered how many painless installs there currently were. Here&#x27;s one more to be counted (WIn2K, Sun Java v5Update6)&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know much about software, but I&#x27;m sure this must have taken a vast amount of your time. Thank you for every moment of it. This program will be useful every day, and I&#x27;ll be passing it on at every chance I get.&lt;&#x2F;p&gt;
&lt;p&gt;Best wishes
Nick Nixon&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Over-engineering gone mad</title>
          <pubDate>Mon, 15 May 2006 15:44:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/05/15/over-engineering-gone-mad/</link>
          <guid>https://artificialworlds.net/blog/2006/05/15/over-engineering-gone-mad/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/05/15/over-engineering-gone-mad/">&lt;p&gt;Picture the scene: you are writing a Java application and you&#x27;re trying to do things right, so you use the java.util.logging code to do your logging. To create a logger you do this:&lt;&#x2F;p&gt;
&lt;p&gt;Logger log = Logger.getLogger( &quot;name&quot; );&lt;&#x2F;p&gt;
&lt;p&gt;Everything goes fine until you want to set the logging level from a command-line argument. You set the logging level like so:&lt;&#x2F;p&gt;
&lt;p&gt;log.setLevel( lev );&lt;&#x2F;p&gt;
&lt;p&gt;Where lev is, e.g. Level.FINE.&lt;&#x2F;p&gt;
&lt;p&gt;When you run your program, none of your log messages appear except the ones that appeared before (SEVERE, WARNING, INFO). Nothing appears to have changed.&lt;&#x2F;p&gt;
&lt;p&gt;You read some API docs, and find out that each Logger has a list of handlers. So you call getHandlers() on your Logger.&lt;&#x2F;p&gt;
&lt;p&gt;It doesn&#x27;t have any.&lt;&#x2F;p&gt;
&lt;p&gt;You take a breath.&lt;&#x2F;p&gt;
&lt;p&gt;You read some more, muttering about Sun engineers having spent too much time at university, and find out that there is a hierarchy of Loggers containing other Loggers.&lt;&#x2F;p&gt;
&lt;p&gt;You simultaneously wonder how that could be useful, and whether some stupid thing is happening here to do with that.&lt;&#x2F;p&gt;
&lt;p&gt;You try log.getParent() and find that it is not null. Eureka! You&#x27;ve got it now:&lt;&#x2F;p&gt;
&lt;p&gt;log.getParent().setLevel( lev );&lt;&#x2F;p&gt;
&lt;p&gt;You hastily recompile, palms sweaty with finally cracking a problem that should not have taken this long.&lt;&#x2F;p&gt;
&lt;p&gt;Nothing has changed. You still don&#x27;t get your log messages.&lt;&#x2F;p&gt;
&lt;p&gt;OK, that&#x27;s fine: you know about handlers now, so it&#x27;s a simple change - we&#x27;ll do this instead:&lt;&#x2F;p&gt;
&lt;p&gt;log.getParent().getHandlers()[0].setLevel( lev );&lt;&#x2F;p&gt;
&lt;p&gt;You recompile, trying not to get your hopes up.&lt;&#x2F;p&gt;
&lt;p&gt;Nothing.&lt;&#x2F;p&gt;
&lt;p&gt;You consider re-writing your application (which has been in development over 5 years) in any other language. You decide to try one more thing before giving up. What if you need to tell both the Logger and the handler what level to use? It would be pretty awkward, but not outside the realms of possibility.&lt;&#x2F;p&gt;
&lt;p&gt;log.getParent().setLevel( lev );
log.getParent().getHandlers()[0].setLevel( lev );&lt;&#x2F;p&gt;
&lt;p&gt;Not even very hopeful now, you recompile.&lt;&#x2F;p&gt;
&lt;p&gt;Nothing.&lt;&#x2F;p&gt;
&lt;p&gt;This is the moment where you go and find anyone who will understand (or failing that anyone at all) and tell them about it. They wipe the spittle from their faces and appear keen to leave.&lt;&#x2F;p&gt;
&lt;p&gt;You will not be defeated by this. You try every combination you can think of. You start looking into how to log a bug with Sun. You curse and curse again.&lt;&#x2F;p&gt;
&lt;p&gt;A very long time later, you know you should have given up, and you&#x27;re trying things just out of bloody-mindedness, and you stumble across this:&lt;&#x2F;p&gt;
&lt;p&gt;log.setLevel( lev );
log.getParent().setLevel( lev );
log.getParent().getHandlers()[0].setLevel( lev );&lt;&#x2F;p&gt;
&lt;p&gt;Yes, that&#x27;s right. To set the log level of the default logger in Java you have to learn about handlers and the logger hierarchy, and you have to set the level in three places.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;THREE PLACES.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You swear never to feel superior again when people start having &quot;language wars&quot; about which programming language is better. They are not all the same.&lt;&#x2F;p&gt;
&lt;p&gt;You engage in a relaxing pasttime, such travelling to Sun&#x27;s development centre and punching the person who &quot;designed&quot; that interface in the face.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Keyboard layout change in Ubuntu</title>
          <pubDate>Thu, 11 May 2006 18:03:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/05/11/keyboard-layout-change-in-ubuntu/</link>
          <guid>https://artificialworlds.net/blog/2006/05/11/keyboard-layout-change-in-ubuntu/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/05/11/keyboard-layout-change-in-ubuntu/">&lt;p&gt;I&#x27;ve had this problem a couple of times in my Ubuntu machine. I installed it with US keyboard layout, and then changed it to UK later (for my user). Even when I select the correct keyboard model (Generic 105-key (Intl) PC), and layout (United Kingdom International) some keys still act weirdly (e.g. pressing &quot; does nothing, and if I press it twice I get a strange &quot;-like symbol). I fixed it by running:&lt;&#x2F;p&gt;
&lt;p&gt;sudo xmodmap &#x2F;usr&#x2F;share&#x2F;xmodmap&#x2F;xmodmap.uk&lt;&#x2F;p&gt;
&lt;h2 class=&quot;comments-title&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andre
    &lt;span class=&quot;comment_date&quot;&gt;2007-08-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Andy! Lost my swedish Ã…Ã¥Ã„Ã¤Ã–Ã¶, and found them here ;–)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-08-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great news Andre – wouldn’t want you to have to survive without them…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Vince
    &lt;span class=&quot;comment_date&quot;&gt;2008-01-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Great! Works fine with a UK Keyboard stuck in French mode for historical reasons…&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Nick Mann
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a href=&quot;http:&#x2F;&#x2F;askubuntu.com&#x2F;questions&#x2F;434849&#x2F;change-keyboard-layout-english-uk-on-command-line-to-english-us&quot; rel=&quot;nofollow&quot;&gt;http:&#x2F;&#x2F;askubuntu.com&#x2F;questions&#x2F;434849&#x2F;change-...&lt;&#x2F;a&gt;&lt;br&#x2F;&gt;
has an answer that worked for me&lt;&#x2F;p&gt;
&lt;p&gt;sudo apt-get install console-common&lt;br&#x2F;&gt;
sudo dpkg-reconfigure console-data&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2015-07-08&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks Nick.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Tony
    &lt;span class=&quot;comment_date&quot;&gt;2008-06-04&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I have a Compaq Presario R3000 lap top. I can not seem to get the layout right no matter what. I purchased it in Canada. I guess the layout is the same as US. Non of my SHIFT characters are correct and i am missing @ from the keyboard entirely. Any suggestions?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Ozbo
    &lt;span class=&quot;comment_date&quot;&gt;2008-07-29&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, this solved my problem when nothing else could. By the way I needed my slovenian configuration by changing last two letters into si. THANKS REALLLLY&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    arun
    &lt;span class=&quot;comment_date&quot;&gt;2008-11-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;thanx dude it ran&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Peter
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I’d like to use ubuntu with at least 3 language layouts without having to write scripts if possible. Is there any option like in windows where you can change with alt-tab?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi Peter, the &quot;Keyboard Indicator&quot; applet might help, but I’ve no idea really I’m afraid.  You can install that by right-clicking on the Panel, choosing &quot;Add to Panel…&quot; and then clicking &quot;Keyboard Indicator&quot;, then &quot;Add&quot;.  Good luck!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Peter
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Muchas gracias :) That’s what i’ve been looking for. If i find a hotkey for the function, i’ll post it here. thx again :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2008-12-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Glad to be of help :)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    jess
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;thanks andy!!:)got really scared for a min!!thank you sooo much&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    gowtham
    &lt;span class=&quot;comment_date&quot;&gt;2009-04-20&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Hi andy and others, i have installed ubuntu in my DELL INSPIRON-1525, my keys are not working inside terminals.. Backspace is not functioning and many more strange symbols.. i have a layout of USA and DELL in my settings.. help me out.. thank you..&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Chris
    &lt;span class=&quot;comment_date&quot;&gt;2009-06-06&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Cheers mate, another problem solved ;)&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Malgosia
    &lt;span class=&quot;comment_date&quot;&gt;2009-06-24&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks, that solved my problem of not having all this bountiful polish characters as well!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    random guy
    &lt;span class=&quot;comment_date&quot;&gt;2009-08-17&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thank You very much !!!!!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    chris lanz
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;I was so glad to get your message about sudo xmodmap&lt;&#x2F;p&gt;
&lt;p&gt;Now at least I can do ‘ and ` and ~ and ^ but now &quot; and @ are reversed. I’ll try some other layouts… .I guess I can learn to substitute shift-2 for shift-&lt;&#x2F;p&gt;
&lt;p&gt;I’m on a Dell Inspiron&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    chris lanz
    &lt;span class=&quot;comment_date&quot;&gt;2009-09-16&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Actually, it seems I’ll have to run the sudo xmodmap every time I boot. Is there any way I can get it into a config.sys or autoexec.bat, to use a couple of ancient terms?&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    ulkas
    &lt;span class=&quot;comment_date&quot;&gt;2009-10-22&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;2chris lanz:&lt;br&#x2F;&gt;
just put it into automatic sessions:&lt;br&#x2F;&gt;
menu-system-preferences-sessions&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    miki
    &lt;span class=&quot;comment_date&quot;&gt;2010-01-26&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;andy balaam, yes!, this was a great help indeed, should be more widely known one, I am using Linux for 10 years and never got to it…Thanks! Croatian and Polish, with mandarin (zhujin), really good.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Pox
    &lt;span class=&quot;comment_date&quot;&gt;2007-03-18&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;the Â¨Â¨ thing is caused by the International layout – pressing ‘ and then a will make Ã¡.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Michael
    &lt;span class=&quot;comment_date&quot;&gt;2007-04-21&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;thanks Andy :–) that solved a few issues&lt;&#x2F;p&gt;
&lt;p&gt;cheers Mate&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Sean
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Thanks. This has been a big help!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andy Balaam
    &lt;span class=&quot;comment_date&quot;&gt;2007-05-15&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Glad to be of service!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;comment&quot;&gt;
&lt;h3&gt;
    Andreas Zieritz
    &lt;span class=&quot;comment_date&quot;&gt;2007-12-01&lt;&#x2F;span&gt;
&lt;&#x2F;h3&gt;
&lt;p&gt;Seems to fix a problem i had on my Norwegian PC as well.&lt;br&#x2F;&gt;
Ã¸Ã¸Ã¸Ã¦Ã¦Ã¦Ã¦Ã†Ã†Ã† back again.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</description>
      </item>
      <item>
          <title>FreeGuide source layout (and SVN migration)</title>
          <pubDate>Tue, 02 May 2006 22:59:58 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/05/02/freeguide-source-layout-and-svn-migration/</link>
          <guid>https://artificialworlds.net/blog/2006/05/02/freeguide-source-layout-and-svn-migration/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/05/02/freeguide-source-layout-and-svn-migration/">&lt;p&gt;Christian and others have pointed out lots of problems with the source code layout in FreeGuide. The biggest problem is that each plugin has its own source tree which makes it very difficult to set up in an IDE like Eclipse. I knew it needed doing, and the other night I couldn&#x27;t sleep and I felt like making it happen. Since SVN is so much better than CVS for re-arranging source code (because you can move files and keep their history) and because sourceforge are now offering SVN, I decided the right thing to do would be to migrate to SVN before making the changes.&lt;&#x2F;p&gt;
&lt;p&gt;So far, I have managed to get all the source code to build from the command line (instructions here: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;Build_from_SVN&quot;&gt;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;Build_from_SVN&lt;&#x2F;a&gt;) and I&#x27;m working on making the Ant build work again, although there&#x27;s still a bit to do there.&lt;&#x2F;p&gt;
&lt;p&gt;My aim is for it to be extremely easy to start developing FreeGuide: just download or check out the source code, fire up your editor or IDE, compile and run. That means FreeGuide needs to handle loading plugins from either JARs or directories (without being told which to do), and it needs to know where to look for its lib and doc directories if they are not specified.&lt;&#x2F;p&gt;
&lt;p&gt;Soon, it will Just Work. Then hopefully this will encourage more developers to get involved.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile I&#x27;ve decided if I want to build up some momentum I need to balance my limited time between code and email, instead of just fire-fighting the email all the time. Some individual users may not get their questions answered, which I hate, but in the end it would be worse if the project lost interest and faded away. I need to inject some excitement!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Letter to Linux Journal</title>
          <pubDate>Fri, 17 Mar 2006 16:06:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/17/letter-to-linux-journal/</link>
          <guid>https://artificialworlds.net/blog/2006/03/17/letter-to-linux-journal/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/17/letter-to-linux-journal/">&lt;p&gt;Dear LJ,&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d like to start by saying thank you very much for the years of quality articles and features that your magazine has provided. I eagerly await the arrival of each month&#x27;s copy as it jets over the Atlantic to my doorstep. LJ has always provided detailed and informative articles on topics both mainstream and more quirky, but always with the deeply technical edge no other magazine or web site I have found can provide.&lt;&#x2F;p&gt;
&lt;p&gt;I am writing because the April issue proved a grave disappointment. Gone were the technical articles and gone were the interesting projects. In their place I found a plethora of opinion pieces most of which provided no new insight into their subject that I could not have found on Slashdot or OSNews, along with some beginner&#x27;s articles on how to install a couple of PHP applications. Even the Doc had nothing interesting to say about the yesterday&#x27;s-news trying-to-be-trendy refrain of &quot;blogs, wikis and podcasts&quot; which seemed to be repeated several times on each page. The articles which did contain decent technical content (notably &quot;Work the shell&quot; and &quot;Remote Temperature Monitoring with Linux&quot;) were swamped beneath the river of fluff through which I found myself wading.&lt;&#x2F;p&gt;
&lt;p&gt;When Nicholas Petreley wrote his first uninformed and ill-judged &quot;&#x2F;etc&#x2F;rant&quot; I felt I could let it pass since it was just one page of an excellent magazine, but now that his style of writing - opinion rather then content - seems to be spreading to the rest of the magazine, I feel the need to express my disquiet at what he has been doing with this column.&lt;&#x2F;p&gt;
&lt;p&gt;&#x2F;etc&#x2F;rant is an article put aside each month for criticising free software projects. The fact that an editor would allow such an article to exist, let alone write it himself, displays a profound and worrying misunderstanding of the community to which he is trying to contribute. Free software projects live and breath through encouragement and just plain getting on with it. As a member of this community, the right way to express dissatisfaction with the way something is being done is to contribute to the project, or (if necessary) launch or encourage an equivalent project that does things differently. By criticising from the sidelines Mr. Petreley reveals himself to be an outsider - as a writer a more appropriate way to contribute to the community (and hence form a useful part of it) would be to praise and encourage projects which are doing the right thing, rather than sucking energy out of valuable and innovative projects which have involved large amounts of volunteer effort such as GCJ and Gnome.&lt;&#x2F;p&gt;
&lt;p&gt;I never thought I would say it, but if this continues I will be cancelling my subscription to LJ. Sadly, I don&#x27;t think I&#x27;ll ever find another magazine that will reach the level of quality I have come to expect from you.&lt;&#x2F;p&gt;
&lt;p&gt;Yours sincerely,&lt;&#x2F;p&gt;
&lt;p&gt;Andy Balaam&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Linux Journal slides</title>
          <pubDate>Tue, 14 Mar 2006 23:52:36 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/14/linux-journal-slides/</link>
          <guid>https://artificialworlds.net/blog/2006/03/14/linux-journal-slides/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/14/linux-journal-slides/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;perkypants.org&#x2F;blog&#x2F;2006&#x2F;03&#x2F;15&#x2F;requiem-for-linux-journal&#x2F;&quot;&gt;jdub notes&lt;&#x2F;a&gt; that Linux Journal is going to the dogs. If the only change were to the back page I could get over it and move on, but so far I am about half way through the latest issue (March 2006?) and I&#x27;ve read nothing but opinion pieces.&lt;&#x2F;p&gt;
&lt;p&gt;I can get all the rantish uninformed opinions I need on OSNews. When I want quality technical articles written by people at the top of their field which cover hardcore problem solving and creative projects, then I turn to LJ. That is why I pay REAL MONEY for a paper journal like this. Because it has substance.&lt;&#x2F;p&gt;
&lt;p&gt;If I have to cancel my subscription, where should I go?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Donation</title>
          <pubDate>Wed, 08 Mar 2006 16:37:45 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/08/donation/</link>
          <guid>https://artificialworlds.net/blog/2006/03/08/donation/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/08/donation/">&lt;p&gt;Forgot to mention: I got my first non-anonymous donation to the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; project - Steve Fuller sent in a donation along with a request (but with no implication of linkage ;). Thanks Steve! It never ceases to amaze me how Americans will donate to projects like this. My feeling about British people (certainly myself) is that I would always think if I were going to donate to something I&#x27;d make it something &quot;important&quot; like Christian Aid, but actually I do think Free Software is important too (actually in order to achieve some of the same goals that Christian Aid have...). The idea of mixing philanthropy with self-interest, which seems to be natural to Americans, can feel a little dirty to us Europeans, but actually that is stupid.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, enough philosophy: Steve, your donation will be used to buy myself something nice (e.g. a GameCube game or some online poker money) and it has significantly encouraged me to get stuck back into &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; development (see the previous post ... guilt is rising). You never know, it might even subconsciously encourage me to get to your specific feature request sooner ;).&lt;&#x2F;p&gt;
&lt;p&gt;(The feature request was allowing favourites to match on programme descriptions and other, more specialised fields. I am wondering about how to keep the UI simple for this, but it should be do-able given some time to implement it.)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide update</title>
          <pubDate>Wed, 08 Mar 2006 16:28:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/08/freeguide-update/</link>
          <guid>https://artificialworlds.net/blog/2006/03/08/freeguide-update/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/08/freeguide-update/">&lt;p&gt;I have very little time for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; at the moment. Part of the problem is that there are lots of boring bugs to fix instead of interesting new features, and it&#x27;s hard to motivate myself to get into it. I am really keen on making it Just Work every time, but since other people have been working on the code and quite a lot of it is unfamiliar, it&#x27;s a lot of work to work out where to look to fix a problem.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully when I&#x27;ve purged this poker thing from my system I will find time to get into the latest &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;dev&#x2F;index.php&#x2F;TODO_list&quot;&gt;TODO list&lt;&#x2F;a&gt;. At the moment all my FreeGuide time is going into trying to keep up with helping people on the mailing lists. Joe Blow is doing a fantastic job of providing an initial response to lots of mails, but often people are encountering weird bugs that need a developer to get involved. It would be great if some of the other people involved in development were able to help out, but it&#x27;s quite a thankless task, and takes away time from developing...&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s frustrating when quite a few of the bugs we have are things I fixed in a previous version that have crept in with new implementations of existing functionality. It&#x27;s taken ages to get FreeGuide stable, and recently it seems to have taken some steps backwards. My feeling is that the percentage of people who are able to install it with no problems and download listings straight away has gone down considerably. I _really_ want to fix this.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why the TODO list for 0.10.4 is pure bugs. We need to make it _work_ before we make it do new stuff. Otherwise people are just going to walk away.&lt;&#x2F;p&gt;
&lt;p&gt;Saying that, the addition of a new experimental vertical listings view by Christian is really exciting. I haven&#x27;t even found time to try it out yet: I must examine my priorities. FreeGuide has always been my &quot;main&quot; project but I have been neglecting it as time presses in.&lt;&#x2F;p&gt;
&lt;p&gt;The Boy (as I call him) is doing well and has slept well the last 2 nights, giving us a break from the all-over-body pain of extended tiredness. If this is part of a trend towards more sleep, I may get more time to make FreeGuide better.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Faster Poker Analysis</title>
          <pubDate>Tue, 07 Mar 2006 17:41:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/07/faster-poker-analysis/</link>
          <guid>https://artificialworlds.net/blog/2006/03/07/faster-poker-analysis/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/07/faster-poker-analysis/">&lt;p&gt;Forgot to mention that I also have one other optimisation up my sleeve: instead of fully analysing each hand I could only do that when I need it i.e. when the hand type is the same, so I could just return &quot;flush&quot; and only if your opponent also has a flush would I need to say &quot;flush ace high&quot; or whatever. This provision of the detail is actually quite time-consuming, so it may be a significant improvement to avoid it in most cases.&lt;&#x2F;p&gt;
&lt;p&gt;Current timings (without the above optimisation) on a P4 2.4GHz machine:&lt;pre&gt;[andy@andy-p4-linux src]$ time .&#x2F;poker 5		# Analyse all possible 5-card hands
Straight flush:  0.001539%
Four of a kind:  0.024010%
Full house:      0.144058%
Flush:           0.196540%
Straight:        0.392465%
Three of a kind: 2.112845%
Pair of pairs:   4.753902%
Pair:            42.256903%
High card:       50.117739%&lt;&#x2F;p&gt;
&lt;p&gt;real    0m4.058s
user    0m3.805s
sys     0m0.002s
[andy@andy-p4-linux src]$ time .&#x2F;poker 7		# Analyse all possible 7-card hands
Straight flush:  0.016811%
Four of a kind:  0.168067%
Full house:      2.555057%
Flush:           3.039766%
Straight:        4.475084%
Three of a kind: 4.873941%
Pair of pairs:   23.512489%
Pair:            43.946865%
High card:       17.411920%&lt;&#x2F;p&gt;
&lt;p&gt;real    3m43.479s
user    3m42.963s
sys     0m0.047s&lt;&#x2F;pre&gt;This is nice, because there are 51 times as many 7-card hands as 5-card ones, and the analysis only takes 55 times as long even though it&#x27;s more complex to analyse larger hands, so it means my analysis scales pretty well with the number of cards.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Poker Analysis</title>
          <pubDate>Tue, 07 Mar 2006 16:59:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/03/07/poker-analysis/</link>
          <guid>https://artificialworlds.net/blog/2006/03/07/poker-analysis/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/03/07/poker-analysis/">&lt;p&gt;I&#x27;ve just requested sourceforge space for my latest mini-project: Poker Analysis. I&#x27;ve been a big fan of poker for years, and am pleased to see everyone is catching up with me finally with the boom of Internet poker. I&#x27;ve started playing a little online, and it set me thinking about the algorithmic aspects of analysing the game. Obviously, the real skills of poker are about predicting people&#x27;s actions, but before you can do that you need to be able to see what hand you&#x27;ve got and what possibilities are out there, and that stuff is entirely deterministic.&lt;&#x2F;p&gt;
&lt;p&gt;I like small, self-contained challenges, so I set about writing a program to play every two-person game of Texas Hold &#x27;Em poker that could ever happen, and ranks the two-card hands you can have in order of how many games they win. Obviously most people have a pretty good idea of which hands are good or bad, but I bet there would be some interesting results to see the full ranking.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, I&#x27;m sure lots of people have done this before, but I want to do it too! Also, of course, it doesn&#x27;t matter how good your hand is in the grand scheme of things, just whether it beats the other hands out there, but bear with me: I&#x27;m interested in the results.&lt;&#x2F;p&gt;
&lt;p&gt;The major challenge is getting it to run fast enough, and that is what I&#x27;m finding interesting. I&#x27;ve already changed my algorithm several times and made huge improvements in execution time, but to run through all the trillion or so (I think) hands would still take several years to execute on my home machine, so I&#x27;ve got some work still to do.&lt;&#x2F;p&gt;
&lt;p&gt;There are 2 major parts to the algorithm: enumerating all the hands you could get, which sounds easy but is in fact slightly tricky, and difficult to do perfectly if you want to avoid repeating redundant analyses, and finding out what hand you have (e.g. pair, flush). I&#x27;m pretty happy with my current implementation of the second, but I&#x27;ve got quite a lot of work to do on the first.&lt;&#x2F;p&gt;
&lt;p&gt;To analyse what hand you&#x27;ve got I&#x27;m taking an approach where all the real work is done when you sort the hand, and then looking for pairs or straights is easy since the cards are already next to each other. I then re-sort the cards so that the same suits are next to each other, and that makes it easy to find flushes. The part I&#x27;m really happy with is that the way I choose hands to analyse means they are already sorted, and when I transform them into the sorted-by-suit instead of by number order, I have very little work to do the way I&#x27;ve done it. (Some slight extra complication is added by the need for aces to be either high or low in straights.) This means that deciding what hand you&#x27;ve got is pretty quick, and I am able to enumerate and analyse every 5-card poker hand (2.6 million hands) in 4 seconds on my home machine.&lt;&#x2F;p&gt;
&lt;p&gt;I believe I might be able to speed up the analysis a little by changing my encoding of cards which at the moment is just consecutive integers (e.g. 0=2c, 1=3c, 2=4c, ..., 12=Ac, 13=2d, ...) but might be better to have each suit starting at a power of two to allow the use of the shift operator instead of %13 or something, but I&#x27;m happy for the moment. Where I need to improve is in writing the code that enumerates all the hands. As I have it at the moment, it plays every 2-card hand against every other 2-card hand for every 5-card set of community cards (but obviously preventing repeats of the same card) but this is hugely redundant (something like 24x?) since the suits are symmetrical. Exactly how much redundancy I can squeeze out of it is what I am thinking about now.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, hopefully soon the code will be in sourceforge CVS, and when I&#x27;ve achieved my goal of examining all games I&#x27;ll make a release. Hopefully in the future I&#x27;ll make it accept as arguments the current situation in a game and output the probabilities of each player winning.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Podcasts I listen to</title>
          <pubDate>Mon, 27 Feb 2006 17:43:24 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/02/27/podcasts-i-listen-to/</link>
          <guid>https://artificialworlds.net/blog/2006/02/27/podcasts-i-listen-to/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/02/27/podcasts-i-listen-to/">&lt;p&gt;I use a modestly-modified &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;linc.homeunix.org:8080&#x2F;scripts&#x2F;bashpodder&#x2F;&quot;&gt;BashPodder&lt;&#x2F;a&gt; to download my podcasts (Internet radio shows) automatically every night, and sort them into different directories. If anyone is interested in my modifications, do ask and I can send them to you. I haven&#x27;t sent them to Linc since there seem to be a million modifications, which probably do everything mine do, but better. The only modestly interesting thing I added was the ability to convert from m4a format to mp3 automatically, and that doesn&#x27;t seem to work, and the Adam Curry PodFinder show is so bad that I realised I didn&#x27;t want to be able to do it anyway (note no link since it&#x27;s really not worth it).&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, that brings me on to my main point, which is that I have subscribed to a lot of podcasts, and unsubscribed to a lot too. I have now honed my list to ones that I really like, so why not share?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.lugradio.org&quot;&gt;LugRadio&lt;&#x2F;a&gt; - excellent Linux show that even my wife enjoys (she is not in the least interested in Linux) because the four (British) presenters are really funny.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.pokerdiagram.org&quot;&gt;Poker Diagram&lt;&#x2F;a&gt; - two British guys playing online poker and talking about it. I&#x27;ve learnt a lot about poker from this, and really enjoy listening to it. Of no interest to someone who doesn&#x27;t know Texas Hold&#x27;Em poker pretty well, as it&#x27;s tricky to follow just by audio description.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;tllts.org&quot;&gt;The Linux Link Tech Show&lt;&#x2F;a&gt; - US Linux show that is broadcast live, and then podcast unedited. Not in the least professional, but entertaining if you are interested in Linux and&#x2F;or like listening to the ramblings of some totally ordinary Americans. It&#x27;s not so often that you get to hear from real people in America, instead of TV stereotypes, and I like that aspect of it as well as some interviews with interesting people.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;fivelive&#x2F;entertainment&#x2F;kermode.shtml&quot;&gt;Mark Kermode&lt;&#x2F;a&gt; - really funny film reviews originally broadcast on the UK&#x27;s Radio 5 (which I would never listen to, since all my radios are permanently tuned to Radio 4). I really trust his opinions, and I love to hear him destroy bad films. It&#x27;s remarkable that a true horror fan has managed to get himself a show on national radio, and not just reviewing horror films, but all films. He, as he is the first to admit, is always right.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;news.bbc.co.uk&#x2F;1&#x2F;hi&#x2F;programmes&#x2F;from_our_own_correspondent&#x2F;default.stm&quot;&gt;From Our Own Correspondent&lt;&#x2F;a&gt; - fascinating &quot;personal reflections&quot; by BBC correspondents who are living in all kinds of interesting places. Journalists, who often have to hold back poetic thoughts to be impartial, are allowed to do whatever they like on this programme, and the results are often very moving, and almost always the kind of thing you talk to people at work about the next day.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.mashuptown.com&quot;&gt;Mashup Town&lt;&#x2F;a&gt; - because I am in with the kids. Occasionally has a really good song e.g. the mashup of Eminem with The White Stripes that was amazing and I accidentally deleted. Email it to me if you&#x27;ve got it.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Basically everything else I&#x27;ve listened to is almost completely not worth it.&lt;&#x2F;p&gt;
&lt;p&gt;Look out this year for loads of corporate AdvertCasts and avoid them.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up my ideal email system 4</title>
          <pubDate>Mon, 27 Feb 2006 17:14:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/02/27/setting-up-my-ideal-email-system-4/</link>
          <guid>https://artificialworlds.net/blog/2006/02/27/setting-up-my-ideal-email-system-4/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/02/27/setting-up-my-ideal-email-system-4/">&lt;h3 id=&quot;sending-mail&quot;&gt;Sending mail&lt;&#x2F;h3&gt;
&lt;p&gt;I used to do something clever with sendmail, but then spamcop blocked my computer because it apparently wasn&#x27;t set up properly, so now I use my ISP&#x27;s smtp server like everyone else. When I need to tunnel in I simply create a tunnel from localhost&#x27;s port 25 to my isp&#x27;s smtp server&#x27;s port 25, and set localhost as my smtp server on the machine from which I am tunneling. It works fine, and there was really no need to use my own sendmail in the first place, it turns out.&lt;&#x2F;p&gt;
&lt;p&gt;Next time: how I made my own webmail server.&lt;&#x2F;p&gt;
&lt;p&gt;TODO for this blog:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;FreeGuide update&lt;&#x2F;li&gt;
&lt;li&gt;My poker analysis program&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>I spawn</title>
          <pubDate>Mon, 06 Feb 2006 21:38:46 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2006/02/06/i-spawn/</link>
          <guid>https://artificialworlds.net/blog/2006/02/06/i-spawn/</guid>
          <description xml:base="https://artificialworlds.net/blog/2006/02/06/i-spawn/">&lt;p&gt;&lt;img src=&quot;http:&#x2F;&#x2F;69.89.27.216&#x2F;%7Eartific2&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;02&#x2F;benjamin.jpg&quot; alt=&quot;Benjamin&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up my ideal email system 3</title>
          <pubDate>Mon, 12 Sep 2005 18:11:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/09/12/setting-up-my-ideal-email-system-3/</link>
          <guid>https://artificialworlds.net/blog/2005/09/12/setting-up-my-ideal-email-system-3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/09/12/setting-up-my-ideal-email-system-3/">&lt;h3 id=&quot;fetchmail-setup&quot;&gt;Fetchmail setup&lt;&#x2F;h3&gt;
&lt;p&gt;My ISP provides my email by POP3, so I use fetchmail to download it every 10 minutes. I created a file .fetchmailrc file in my home directory that looks like this:&lt;&#x2F;p&gt;
&lt;p&gt;# Configuration created Wed Jul 27 06:43:07 2005 by fetchmailconf
set postmaster &quot;andy&quot;
set bouncemail
set no spambounce
set properties &quot;&quot;
poll mail.myisp.co.uk with proto POP3
user &#x27;andy@myisp.co.uk&#x27; there with password &#x27;password&#x27; is &#x27;andy&#x27; here&lt;&#x2F;p&gt;
&lt;p&gt;This tells fetchmail to use the username and password given to me by my ISP to download mail into the default location for the user andy on the local machine. This would normally put my mail into &#x2F;var&#x2F;spool&#x2F;mail&#x2F;andy, or something like that, but to get it into my Maildir (which I talked about in a &lt;a href=&quot;&#x2F;blog&#x2F;2005&#x2F;08&#x2F;19&#x2F;setting-up-my-ideal-email-system&#x2F;&quot;&gt;previous blog entry&lt;&#x2F;a&gt;), I added these lines to a .procmailrc in my home directory:&lt;&#x2F;p&gt;
&lt;p&gt;MAILDIR=$HOME&#x2F;Maildir&#x2F;
DEFAULT=$MAILDIR
LOGFILE=$MAILDIR&#x2F;log&lt;&#x2F;p&gt;
&lt;p&gt;Now when I type the command fetchmail (logged in as andy) I get something like this:&lt;&#x2F;p&gt;
&lt;p&gt;$ fetchmail
fetchmail: Server CommonName mismatch: localhost != mail.myisp.co.uk
fetchmail: Server CommonName mismatch: localhost != mail.myisp.co.uk
fetchmail: Server CommonName mismatch: localhost != mail.myisp.co.uk
fetchmail: No mail for andy@myisp.co.uk at mail.myisp.co.uk&lt;&#x2F;p&gt;
&lt;p&gt;(No idea how to get rid of those errors, but they don&#x27;t seem to cause any problems.) And if any mail was found, it appears in my Inbox in Thunderbird, as if by magic.&lt;&#x2F;p&gt;
&lt;p&gt;Next time: how I send mail, using sendmail.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide 0.10pre2</title>
          <pubDate>Mon, 12 Sep 2005 17:59:43 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/09/12/freeguide-010pre2/</link>
          <guid>https://artificialworlds.net/blog/2005/09/12/freeguide-010pre2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/09/12/freeguide-010pre2/">&lt;p&gt;So I&#x27;ve put out &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; version 0.10pre2, and then a little bug-fix release because the Linux RPMs were broken. By calling this 0.10pre2-2 I managed to annoy my faithful Gentoo packager Christian, and myself, but at the time it seemed too much hassle to bump everytjing up to 0.10pre3.&lt;&#x2F;p&gt;
&lt;p&gt;The next release, 0.10.3, &lt;strong&gt;is&lt;&#x2F;strong&gt; going to be stable, and everyone is finally going to be using and testing our latest code stream. We&#x27;ve had a few bugs reported again 0.10pre2, and we&#x27;ve already got a couple to fix, and then we&#x27;re going to push it out asap and get everyone migrated up.&lt;&#x2F;p&gt;
&lt;p&gt;I hope people will like the new version - it&#x27;s a lot faster, and the UI experience is better (in my opinion). I just hope there aren&#x27;t too many wrinkles in the upgrade path.&lt;&#x2F;p&gt;
&lt;p&gt;Alex has written some good upgrade code, but some of the concepts have changed, and I&#x27;m not entirely happy with the depths you have to delve into the Options screen just to play with your XMLTV grabber, but most things should &quot;Just Work&quot; for normal users.&lt;&#x2F;p&gt;
&lt;p&gt;After 0.10.3, I really want to get recording working, using Reuel&#x27;s plugin. If anyone wants to send me a UK digital TV card, you can be sure it would be put to good use...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up my ideal email system 2</title>
          <pubDate>Wed, 31 Aug 2005 20:19:10 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/08/31/setting-up-my-ideal-email-system-2/</link>
          <guid>https://artificialworlds.net/blog/2005/08/31/setting-up-my-ideal-email-system-2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/08/31/setting-up-my-ideal-email-system-2/">&lt;h3 id=&quot;setting-up-mozilla-thunderbird&quot;&gt;Setting up Mozilla Thunderbird&lt;&#x2F;h3&gt;
&lt;p&gt;On the same machine that was running Dovecot, I set up &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.mozilla.org&#x2F;products&#x2F;thunderbird&#x2F;&quot;&gt;Thunderbird&lt;&#x2F;a&gt; to be able to access my email. I did this by creating a new email account (which I called &quot;Andy IMAP&quot;), choosing &quot;localhost&quot; as the server, and entering the username I created in &#x2F;etc&#x2F;imap.passwd (&#x27;brian&#x27; in the example I gave) as the username. I set up all the other settings as I wanted them, and clicked OK.&lt;&#x2F;p&gt;
&lt;p&gt;When I clicked &quot;Get New Messages,&quot; Thunderbird asked for my password, and I entered the password I had put into &#x2F;etc&#x2F;imap.passwd (&#x27;sausages&#x27; in the example I gave). Now I could see some folders had appeared under the new account I had created, but there was nothing in them.&lt;&#x2F;p&gt;
&lt;p&gt;I tested whether I could read and write emails to the account by dragging an email from another folder (in an old account) to the &quot;Inbox&quot; folder in the new account &quot;Andy IMAP&quot;. It copied OK, and when I clicked on Inbox I could see the email in the list, and when I clicked the email its contents appeared in the preview pane. Success! My IMAP account existed.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, how I made emails that people sent to me appear in my new Inbox.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Setting up my ideal email system</title>
          <pubDate>Fri, 19 Aug 2005 19:23:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/08/19/setting-up-my-ideal-email-system/</link>
          <guid>https://artificialworlds.net/blog/2005/08/19/setting-up-my-ideal-email-system/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/08/19/setting-up-my-ideal-email-system/">&lt;p&gt;Recently I have managed to get pretty much my ideal email system set up. This is the kind of thing that would simply be impossible on a Windows box, but (when you know how) is a quick job on Linux. I didn&#x27;t know how, but now I do (ish). I&#x27;m going to do it in separate installments, trying to make them slightly self-contained.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;setting-up-an-imap-server&quot;&gt;Setting up an IMAP server&lt;&#x2F;h3&gt;
&lt;p&gt;I chose &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.dovecot.org&#x2F;&quot;&gt;Dovecot&lt;&#x2F;a&gt; because it looked fairly good, and it came high up on Google. Installing it on Fedora Core 3 was just:&lt;&#x2F;p&gt;
&lt;p&gt;yum install dovecot&lt;&#x2F;p&gt;
&lt;p&gt;But then I needed to set up the config file. The only thing I changed looked like this:&lt;&#x2F;p&gt;
&lt;p&gt;default_mail_env = maildir:~&#x2F;Maildir&lt;&#x2F;p&gt;
&lt;p&gt;I did this because after reading the scare stories about locking when using an mbox-type mailbox, I decided Maildir sounded safer. Maildir holds each mail in a separate file, which sounds good for preventing corruption if something goes wrong, so I chose it. It seems to work ok.&lt;&#x2F;p&gt;
&lt;p&gt;I left the passwords to be plain text for the moment, which meant my &#x2F;etc&#x2F;imap.passwd had to look like this:&lt;&#x2F;p&gt;
&lt;p&gt;brian:{PLAIN}sausages:500:500::&#x2F;home&#x2F;brian&lt;&#x2F;p&gt;
&lt;p&gt;Where &#x27;brian&#x27; would be your username and &#x27;sausages&#x27; the password you have decided to use for mail. If you&#x27;re using plain text passwords, don&#x27;t use the same password as your login password, because any old packet sniffer will be able to see the mail password.&lt;&#x2F;p&gt;
&lt;p&gt;With this set up, I had a working IMAP server which stored messages in my home dir under the ~&#x2F;Maildir directory. Of course, there were no messages in there at the moment - that&#x27;s coming up later. Next time, how I made Mozilla Thunderbird see the messages stored there!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Just a little bit longer...</title>
          <pubDate>Tue, 09 Aug 2005 15:56:21 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/08/09/just-a-little-bit-longer/</link>
          <guid>https://artificialworlds.net/blog/2005/08/09/just-a-little-bit-longer/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/08/09/just-a-little-bit-longer/">&lt;p&gt;Now that my Christian youth holiday is over, I&#x27;ve got just one more major job to do before I can get back to working on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt;: my thesis corrections. I don&#x27;t know how long it&#x27;s going to take, but it will certainly be painful. It might be best to avoid me for a few weeks if you don&#x27;t like &quot;grumpy Andy&quot;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>BashPodder</title>
          <pubDate>Fri, 22 Jul 2005 15:08:54 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/22/bashpodder/</link>
          <guid>https://artificialworlds.net/blog/2005/07/22/bashpodder/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/22/bashpodder/">&lt;p&gt;I really must get &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;linc.homeunix.org:8080&#x2F;scripts&#x2F;bashpodder&#x2F;&quot;&gt;BashPodder&lt;&#x2F;a&gt; working, not only to download my two favourite Linux shows &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.thelinuxlink.net&#x2F;tllts&#x2F;&quot;&gt;TLLTS&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.lugradio.org&#x2F;&quot;&gt;LugRadio&lt;&#x2F;a&gt; but also because &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;radio4&#x2F;&quot;&gt;Radio 4&lt;&#x2F;a&gt; has started making &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.bbc.co.uk&#x2F;radio4&#x2F;today&#x2F;&quot;&gt;Today&lt;&#x2F;a&gt; and the best radio programme out there, &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;news.bbc.co.uk&#x2F;1&#x2F;hi&#x2F;programmes&#x2F;from_our_own_correspondent&#x2F;default.stm&quot;&gt;From Our Own Correspondent&lt;&#x2F;a&gt; available for podcast download.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Python objects in C++</title>
          <pubDate>Tue, 19 Jul 2005 15:12:30 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/19/python-objects-in-c/</link>
          <guid>https://artificialworlds.net/blog/2005/07/19/python-objects-in-c/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/19/python-objects-in-c/">&lt;p&gt;Sometime I find myself thinking in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.python.org&#x2F;&quot;&gt;Python&lt;&#x2F;a&gt;. Or, accurately, wistfully thinking of Python. Raw C++ is completely hamstrung when it comes to simple concepts like handling strings or dealing with lists (maybe &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.boost.org&quot;&gt;BOOST&lt;&#x2F;a&gt; is better?). Why can&#x27;t I just do this:&lt;&#x2F;p&gt;
&lt;p&gt;remaining_args = sys.argv[3:7]&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t even mind if it looks like this:&lt;&#x2F;p&gt;
&lt;p&gt;PyList argv = new PyList( argv, argc );
PyList remaining_args = argv.slice( 3, 7 );&lt;&#x2F;p&gt;
&lt;p&gt;Is this kind of thing possible? If not, someone really should package up the Python library in such a way that it is.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, I&#x27;m speaking from a position of total ignorance here: maybe someone has done what I&#x27;m talking about with the Python stuff. After a brief look at BOOST, I can say for sure that it continues the STL&#x27;s tactic of &quot;under-use through obscurity&quot; and &quot;surprisingly missing all the useful features&quot;. No slice operator, anyway...&lt;&#x2F;p&gt;
&lt;p&gt;If they have done&#x2F;do do the Python thing, the most frustrating part then is going to be wishing you could have all the bits of the Python library that are written in Python. Does anyone fancy starting the &quot;GCP&quot; project?&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>CD writing on Fedora Core 3</title>
          <pubDate>Sun, 17 Jul 2005 18:06:04 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/17/cd-writing-on-fedora-core-3/</link>
          <guid>https://artificialworlds.net/blog/2005/07/17/cd-writing-on-fedora-core-3/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/17/cd-writing-on-fedora-core-3/">&lt;p&gt;I&#x27;m now thing of moving off Fedora Core 3 (probably to Ubuntu, or maybe FC4 for the Java stuff) and it turns out I&#x27;ve never written a CD from it, because when I tried it didn&#x27;t work: I kept getting the message &quot;cdrecord: Success. Cannot open SCSI driver.&quot; I finally managed to find a solution. For some reason I need to do this:&lt;&#x2F;p&gt;
&lt;p&gt;cdrecord dev=ATA:1,1,0 -eject -v speed=2 ubuntu-5.04-live-i386.iso&lt;&#x2F;p&gt;
&lt;p&gt;Note the ATA: in the dev bit. No idea why.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Prince of Persia - The Sands of Time</title>
          <pubDate>Fri, 15 Jul 2005 16:47:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/15/prince-of-persia-the-sands-of-time/</link>
          <guid>https://artificialworlds.net/blog/2005/07/15/prince-of-persia-the-sands-of-time/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/15/prince-of-persia-the-sands-of-time/">&lt;p&gt;arrived through the post from ebay yesterday. It is &lt;strong&gt;superb&lt;&#x2F;strong&gt;. Exactly my kind of game, and with loads of features from the old game (one of my favourites of all time), all updated and made even more beautiful. It is incredibly atmospheric, and so far the gameplay has been utterly compelling. Am I more excited about this than the pervious post? Surely not.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Personal stuff is boring</title>
          <pubDate>Fri, 15 Jul 2005 16:46:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/15/personal-stuff-is-boring/</link>
          <guid>https://artificialworlds.net/blog/2005/07/15/personal-stuff-is-boring/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/15/personal-stuff-is-boring/">&lt;p&gt;...and I am not a big fan of pictures in blogs, but this is a special occasion:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;02&#x2F;scan12weeks-small-annotated.jpg&quot; alt=&quot;Scan&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;World domination is nigh. Soon we shall all be Balaams.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Sourceforge</title>
          <pubDate>Wed, 13 Jul 2005 15:46:29 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/13/sourceforge/</link>
          <guid>https://artificialworlds.net/blog/2005/07/13/sourceforge/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/13/sourceforge/">&lt;p&gt;I have requested a &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;sourceforge.net&quot;&gt;Sourceforge&lt;&#x2F;a&gt; project for &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;diyblog&quot;&gt;DIYBlog&lt;&#x2F;a&gt;, and it just struck me again how utterly fantastic Sourceforge is. What would the Free and Open Source Software world be like now without Sourceforge? A lot poorer, I reckon.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m desperately trying to find time to test &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt; for a 0.10.2 release. So far it looks like although my favourites have been imported, they are not working. In fact, when I create a new favourite it works for that run but when I restart the program they are not highlighted. Hmm.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>London</title>
          <pubDate>Fri, 08 Jul 2005 15:23:01 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/08/london/</link>
          <guid>https://artificialworlds.net/blog/2005/07/08/london/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/08/london/">&lt;p&gt;London is a little quieter than normal this morning, but still pretty busy. It seems like most people are going to work, although some are having to walk. There are definitely fewer tourists on the streets.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>London terrorist attacks</title>
          <pubDate>Thu, 07 Jul 2005 19:46:28 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/07/london-terrorist-attacks/</link>
          <guid>https://artificialworlds.net/blog/2005/07/07/london-terrorist-attacks/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/07/london-terrorist-attacks/">&lt;p&gt;As a largely unmilitarised country, Britain (like many other Western countries) is a soft target for terrorists. But that very softness makes attacking us in this way pointless: it&#x27;s like punching a sponge. We won&#x27;t change - we like our country how it is.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s possible that by drawing attention to themselves, the people who did this may have changed some aspect of the world, but as for advancing their cause - how can they when the only discernable motive they have is hatred? Does murder really win them support at home? I doubt it.&lt;&#x2F;p&gt;
&lt;p&gt;We in Britain have done and continue to do some terrible things. I wish we would stop, but today isn&#x27;t going to make any difference either way.&lt;&#x2F;p&gt;
&lt;p&gt;If you or anyone you know was hurt today, I&#x27;m sorry.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>The dangers of learning to code</title>
          <pubDate>Thu, 07 Jul 2005 15:45:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/07/the-dangers-of-learning-to-code/</link>
          <guid>https://artificialworlds.net/blog/2005/07/07/the-dangers-of-learning-to-code/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/07/the-dangers-of-learning-to-code/">&lt;p&gt;I was speaking to someone over the weekend who works in HR and has been enjoying making various Microsoft Access databases to do useful things for work. I warned her she might get the programming bug and then never be able to stop. Then I realised she already had it when she summed it up beautifully:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;You can make it &lt;em&gt;do&lt;&#x2F;em&gt; things.&quot;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I love coding.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Train gaming</title>
          <pubDate>Wed, 06 Jul 2005 15:34:15 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/06/train-gaming/</link>
          <guid>https://artificialworlds.net/blog/2005/07/06/train-gaming/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/06/train-gaming/">&lt;p&gt;For my train journey, I ended up downloading a few emulators that didn&#x27;t work, and a few games that didn&#x27;t work, but what did work was the PC version of Prince of Persia, and the PC version of Cannon Fodder, both of which were very enjoyable.&lt;&#x2F;p&gt;
&lt;p&gt;Especially good were some new levels someone had created for PoP - a lot harder than the original ones! It did show how much work went into creating games, even back then, though, since there were a couple of subtle bugs in the new levels that led to you getting trapped with no means of killing yourself, which means your entire game is over and you have to start again, which is quite frustrating.&lt;&#x2F;p&gt;
&lt;p&gt;I did dig out the code for my track and field game, but when it appeared all on one line with weird symbols for the carriage returns, and I contemplated the idea of fixing this (and coding generally) in notepad, I decided to go back to PoP instead.&lt;&#x2F;p&gt;
&lt;p&gt;More on the track and field game later. It&#x27;s in Python...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Constantly changing passwords</title>
          <pubDate>Mon, 04 Jul 2005 15:24:38 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/04/constantly-changing-passwords/</link>
          <guid>https://artificialworlds.net/blog/2005/07/04/constantly-changing-passwords/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/04/constantly-changing-passwords/">&lt;p&gt;I know it&#x27;s supposed to be best practice to force users to change their passwords regularly, but how are we supposed to remember them? I wonder whether anyone has done any research into passwords becoming lower quality as people are asked to change them. It just becomes increasingly difficult to think up memorable things that are not obvious, or look for ways of fooling the computer into letting you have a similar password every month.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Gaming on the move</title>
          <pubDate>Fri, 01 Jul 2005 15:14:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/07/01/gaming-on-the-move/</link>
          <guid>https://artificialworlds.net/blog/2005/07/01/gaming-on-the-move/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/07/01/gaming-on-the-move/">&lt;p&gt;We&#x27;re taking a 4.5 hour train ride to Leeds at the weekend, and then even longer on the way home, so I am hoping to entertain myself using a Windows 2000 laptop with no CD drive. The options I&#x27;m thinking of are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;pingus.seul.org&#x2F;&quot;&gt;Pingus&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.geocities.com&#x2F;princeofpersia123&#x2F;&quot;&gt;Prince of Persia&lt;&#x2F;a&gt; on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.winuae.net&#x2F;&quot;&gt;WinUAE&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Continuing work on my secret Track and Field game by porting the UI stuff to Windows.&lt;&#x2F;li&gt;
&lt;li&gt;Booting some kind of Linux off my iRiver. Not at all sure this is possible.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The easy way would have been to run &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;games-knoppix.unix-ag.uni-kl.de&#x2F;&quot;&gt;Games Knoppix&lt;&#x2F;a&gt;, but there&#x27;s no CD drive. Gutted.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>ATV Palm export</title>
          <pubDate>Tue, 28 Jun 2005 20:10:51 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/28/atv-palm-export/</link>
          <guid>https://artificialworlds.net/blog/2005/06/28/atv-palm-export/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/28/atv-palm-export/">&lt;p&gt;We&#x27;ve already had a bug report&#x2F;feature request for the ATV Palm plugin Alex wrote, so it&#x27;s obviously going to be pretty popular. It&#x27;s really great that Alex is doing so much work on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; at the moment. We are having our disagreements here and there, but &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt; is getting a lot better all the time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Nearing a new release</title>
          <pubDate>Tue, 28 Jun 2005 20:02:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/28/nearing-a-new-release/</link>
          <guid>https://artificialworlds.net/blog/2005/06/28/nearing-a-new-release/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/28/nearing-a-new-release/">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt; is hopefully nearing a new release. Alex has been working on the plugins system, and hopefully I will be able to test it this week. If it works ok, we&#x27;ll release it as unstable, and then with a few bug fixes we should be able to make the next release stable. Alex has refactored the code so that the main program is just another plugin. This is pretty cool as it means we can upgrade everything without restarting the program (hopefully, although in practice we think we may hit problems with resources not being freed). We are currently discussing exactly who should be able to upgrade what plugin when. I am keen that users be able to get a new listings grabber as soon as they need one without needing their admin to do it for them. On the other hand, probably only the admin should be able to upgrade the main app. The way &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.jedit.org&quot;&gt;jEdit&lt;&#x2F;a&gt; does these things seems to work pretty well.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>DIYPlanet</title>
          <pubDate>Fri, 24 Jun 2005 08:27:34 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/24/diyplanet/</link>
          <guid>https://artificialworlds.net/blog/2005/06/24/diyplanet/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/24/diyplanet/">&lt;p&gt;When I wrote &lt;a href=&quot;..&#x2F;diyblog&quot;&gt;DIYBlog&lt;&#x2F;a&gt; it occurred to me that what I was writing would translate into a blog aggregator pretty easily, and so I&#x27;m working on making that so. I&#x27;d like to start a little community of Java developers who are working on small free and open source software Java programs, so we can start to communicate through our blogs and help each other out with thorny issues e.g. how to get our programs working with an all free software systems (more on that later).&lt;&#x2F;p&gt;
&lt;p&gt;For the moment I&#x27;m working on aggregating my own favourite feeds, which you can see here: &lt;a href=&quot;&#x2F;planetandy&quot;&gt;Planet Andy&lt;&#x2F;a&gt;. All this is&#x2F;will be running on pure FTP web space, with a little cron job running on my own machine to keep it updated. Of course, that does mean it only works when my machine is turned on...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Re-implementing</title>
          <pubDate>Thu, 23 Jun 2005 20:26:11 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/23/re-implementing/</link>
          <guid>https://artificialworlds.net/blog/2005/06/23/re-implementing/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/23/re-implementing/">&lt;p&gt;Been discussing with Alex about the dangers of re-implementing - he wants to simplify the command-line arguments code in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt;, but rather than modifying the existing code, he&#x27;s re-written it. The problem with re-writing is that old code usually has lots of non-obvious bug fixes in it which you will have to re-implement all over again as they are reported. Alex has re-written a few things, and generally he&#x27;s improved the code structure, but we have both then had extra work to do fixing the bugs that are inevitably introduced. That can seem like a waste of time if they&#x27;re bugs that I have already fixed in the past.&lt;&#x2F;p&gt;
&lt;p&gt;It always feels easier to write your own new version instead of understanding what&#x27;s already there, but in reality, unless you&#x27;re changing the output, it&#x27;s never a good idea completely to re-write the internals. There&#x27;s a good &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.joelonsoftware.com&#x2F;articles&#x2F;fog0000000069.html&quot;&gt;article by Joel Spolsky&lt;&#x2F;a&gt; on this.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Honestly my last post about my viva</title>
          <pubDate>Wed, 22 Jun 2005 18:09:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/22/honestly-my-last-post-about-my-viva/</link>
          <guid>https://artificialworlds.net/blog/2005/06/22/honestly-my-last-post-about-my-viva/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/22/honestly-my-last-post-about-my-viva/">&lt;p&gt;...for a while.&lt;&#x2F;p&gt;
&lt;p&gt;Woke up at 4.30am today having awful dreams about how none of the code worked. My dreams transposed the problems I am trying to solve at work onto the code I need to write for my corrections. I couldn&#x27;t stand it any longer so I got up and started work on it, and I&#x27;ve launched an experiment now which should hopefully provide all I need in terms of experimental work. So, it&#x27;s not as bad as my dreams...&lt;&#x2F;p&gt;
&lt;p&gt;The idea of getting back into the writing part also terrifies me. I guess another few weeks of getting up in the small hours might get it done, but I&#x27;m likely to lose all my friends. Obsessing about this is already making me totally inward-focussed. I am trying to make an effort to think about what other people might be feeling, to try and keep my self-pity in check.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Corrections</title>
          <pubDate>Tue, 21 Jun 2005 15:07:16 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/21/corrections/</link>
          <guid>https://artificialworlds.net/blog/2005/06/21/corrections/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/21/corrections/">&lt;p&gt;Well, I passed, so I should be happy, but I&#x27;ve got to do some corrections before I become a doctor, so I&#x27;m feeling disappointed, but really I&#x27;m very close to finally achieving what I&#x27;ve worked so hard for. Just one last push.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Viva today</title>
          <pubDate>Mon, 20 Jun 2005 12:56:56 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/20/viva-today/</link>
          <guid>https://artificialworlds.net/blog/2005/06/20/viva-today/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/20/viva-today/">&lt;p&gt;Today I find out whether I get a PhD. I&#x27;ve been strangely not-nervous about it, although I&#x27;ve woken up pretty early this morning. It&#x27;s difficult to `revise&#x27; since most of what we&#x27;ll be talking about is what I wrote. I&#x27;ve been trying to refresh my memory of papers I cited (especially those of my examiners...) and of what I wrote, but it&#x27;s been so boring I haven&#x27;t done much. Let&#x27;s hope I don&#x27;t regret it later.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m fairly hopeful about passing - I would hope my supervisors would not have let me submit if it wasn&#x27;t going to pass, but I see the viva as being both a rite of passage everyone has to go through (i.e. a horrible experience that my examiners had to go through, so they want to inflict it on me, and a negotiation process about how many corrections I need to do.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not worried about having a hard time today: I tend to stand up reasonably well under face-to-face pressure like this, and I hope I can robustly defend points of which I am confident, and give way in places where I should refine my thinking. I&#x27;m more worried about the corrections. I just can&#x27;t imagine getting back into writing my thesis - I&#x27;ve left it so far behind my thinking, I&#x27;m so sick of it, that the idea of writing another chapter or two is horrific. It was bad enough the first time.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Mnemonic</title>
          <pubDate>Fri, 17 Jun 2005 18:53:07 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/17/mnemonic/</link>
          <guid>https://artificialworlds.net/blog/2005/06/17/mnemonic/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/17/mnemonic/">&lt;p&gt;I made a page for the band Pete Beresford and me had when we were at uni. You can download the 3 songs I was getting around to sending to John Peel. If you like your metal instrumental and a tiny bit Propellorheads, it might be for you: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.artificialworlds.net&#x2F;mnemonic&quot;&gt;Mnemonic&lt;&#x2F;a&gt;. If you like it, let me know and I&#x27;ll send you some &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;flac.sourceforge.net&#x2F;&quot;&gt;FLAC&lt;&#x2F;a&gt; recordings of those songs (still not great quality, I&#x27;m afraid), and&#x2F;or some of our other songs.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide&#x27;s TODO list</title>
          <pubDate>Thu, 16 Jun 2005 23:19:19 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/16/freeguides-todo-list/</link>
          <guid>https://artificialworlds.net/blog/2005/06/16/freeguides-todo-list/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/16/freeguides-todo-list/">&lt;p&gt;Take a look at the latest &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;cvs.sourceforge.net&#x2F;viewcvs.py&#x2F;*checkout*&#x2F;freeguide-tv&#x2F;freeguide-tv&#x2F;doc&#x2F;TODO?rev=HEAD&amp;amp;content-type=text&#x2F;plain&quot;&gt;TODO&lt;&#x2F;a&gt; list. There are several categories of things on it:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Already done e.g. &quot;Add firefox to the possible browsers.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Completeness features e.g. &quot;Use Java&#x27;s printing capabilities to print&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Feature creep e.g. &quot;Automatically make a channel set when you right-click a channel and choose
&quot;Move up&quot; (or even drag!)&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Difficult-to-trace bugs e.g. &quot;Make it not say no listings after midnight&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Difficult features e.g. &quot;Minimize to system tray&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Subtle UI improvements e.g. &quot;Remember where you were in the Options screen&quot;&lt;&#x2F;li&gt;
&lt;li&gt;Major features we are in the middle of e.g. &quot;Run an exe when a programme is about to start and&#x2F;or end&quot; (this is actually recording)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I should go through and make sure everything is relevant, but also I need to prioritise what to do before 1.0 if we are ever going to get there. 7s are being done by Alex and others, 2s really need doing if we are going to call it finished, 4s should get cleared up during the beta stage (hopefully), most 5s are likely to get dropped, and 3s and 6s are good potentials for a public vote - then the most wanted ones will get done and the others will have to be post-1.0.&lt;&#x2F;p&gt;
&lt;p&gt;I may go through the list and actually classify them this way, and then we can see what we&#x27;re doing.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>FreeGuide packaging and plugins</title>
          <pubDate>Wed, 15 Jun 2005 14:53:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/15/freeguide-packaging-and-plugins/</link>
          <guid>https://artificialworlds.net/blog/2005/06/15/freeguide-packaging-and-plugins/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/15/freeguide-packaging-and-plugins/">&lt;p&gt;I couldn&#x27;t sleep last night, I think because my viva for my DPhil (PhD) is coming up on Monday. Work is going to be a struggle, but at least I got a chance to think about &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;&quot;&gt;FreeGuide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Alex is working on a plugins system (it&#x27;s sort-of-working in CVS) that will allow us to split the grabbers out from the main program, and allow people to innovate with new plugins without delving into all the code, which is quite large and a bit messy to say the least. Both of these things are really good, especially because my own time is very limited, so allowing people to work on plugins seperately means progress can continue without me being such a bottleneck.&lt;&#x2F;p&gt;
&lt;p&gt;However, handling plugins means we need to change the way FreeGuide is packaged. I&#x27;ve had some thoughts about how to do it, and I&#x27;ve put them together into an &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;packaging_and_plugins.sxi&quot;&gt;OpenOffice.org Impress presentation&lt;&#x2F;a&gt; (or try the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sourceforge.net&#x2F;packaging_and_plugins.pdf&quot;&gt;PDF&lt;&#x2F;a&gt;). I&#x27;m looking for comments on this before we finalise the system. Once it&#x27;s established it will be hard to change, so I want to try and get it right.&lt;&#x2F;p&gt;
&lt;p&gt;If there are a couple more sleepless nights we might even see FreeGuide&#x27;s 0.10.x branch going stable ... Alex has a few more bugs to fix, but we&#x27;re getting there.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>DIYBlog</title>
          <pubDate>Tue, 14 Jun 2005 16:17:47 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/14/diyblog/</link>
          <guid>https://artificialworlds.net/blog/2005/06/14/diyblog/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/14/diyblog/">&lt;p&gt;I&#x27;ve made a page about how I&#x27;m making this blog, here: &lt;a href=&quot;https:&#x2F;&#x2F;artificialworlds.net&#x2F;blog&#x2F;2005&#x2F;06&#x2F;14&#x2F;diyblog&#x2F;diyblog&#x2F;diyblog.html&quot;&gt;DIYBlog&lt;&#x2F;a&gt;. I may even get around to putting it on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freshmeat.net&quot;&gt;freshmeat&lt;&#x2F;a&gt; at some point, and if others want to contribute I may even put it on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.sourceforge.net&quot;&gt;SourceForge&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>RSS working?</title>
          <pubDate>Mon, 13 Jun 2005 23:17:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/13/rss-working/</link>
          <guid>https://artificialworlds.net/blog/2005/06/13/rss-working/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/13/rss-working/">&lt;p&gt;I think I&#x27;ve managed to work out a system to make RSS work. Basically I write my blog entries into a raw RSS file, and then run a little Python script to add ids, links etc. and generate an HTML page from that. It&#x27;s been a hassle to write (mainly due to timezones, which I&#x27;ve now decided to ignore) and if anyone&#x27;s interested in some highly-customisable blogging software that only requires FTP on your web host, get in touch, especially if you&#x27;re the kind of person who thinks its cool to hand-code your RSS.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Prediction: corporate Linux</title>
          <pubDate>Thu, 09 Jun 2005 20:23:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/09/prediction-corporate-linux/</link>
          <guid>https://artificialworlds.net/blog/2005/06/09/prediction-corporate-linux/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/09/prediction-corporate-linux/">&lt;p&gt;By Thursday 9th June 2015 30% of corporate desktop computers will be running operating systems (and desktop environments) which satisfy the &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.opensource.org&#x2F;docs&#x2F;definition.php&quot;&gt;Open Source Definition&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Guilty Expression</title>
          <pubDate>Wed, 08 Jun 2005 16:03:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/08/guilty-expression/</link>
          <guid>https://artificialworlds.net/blog/2005/06/08/guilty-expression/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/08/guilty-expression/">&lt;p&gt;Having previously been the anonymous benevolent dictator of &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.guiltyexpression.org&quot;&gt;Guilty Expression&lt;&#x2F;a&gt; I have decided to become non-anonymous and I&#x27;ll just have to make sure what I write there is either acceptable or I&#x27;ll have to log out before I write it. Guilty Expression is a web site for people who want somewhere to express the thoughts and feelings they have about God and Christianity that they can&#x27;t express in their church (if they have one) or with people in real life.&lt;&#x2F;p&gt;
&lt;p&gt;I set up the sire after talking to several of my friends who used to be church-going Christians, but were hurt in one way or another (by their church, or by God in the sense of things happening to them) and are now almost-Christians, or ex-Christians but who still think a lot about these things and want to talk about them without being branded unsound or a heretic. A couple of these people have very genuine relationships with God, which, by their very messed-up-ness are very real, but they obviously suffer from not being part of a community. GE is supposed to provide a way back in to talking to people like them and people who feel more confident in their faith (but are very tolerant of those who aren&#x27;t).&lt;&#x2F;p&gt;
&lt;p&gt;GE is also a place for Christians to complain about their churches and the attitudes of other Christians, and other people, and almost to let off steam about things in a way that does not have very bad consequences in terms of hurting people or causing division in their church or group.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve had articles written by people of lots of different ages and backgrounds (from pastors to non-Christians) on loads of topics (from anorexia to the bible to science) and lots of different viewpoints.&lt;&#x2F;p&gt;
&lt;p&gt;My excuse for writing about it on what I hope to be a mainly technical blog is that it presents an interesting challenge in terms of how to build a community. The site is supposed to be for people who are feeling disposessed and out of it, and so they&#x27;re not likely to be looking for it. The vast majority of visitors and contributors to the site are people I have told about it personally, and it has spread a little from there by word of mouth. My being anonymous has not helped, because it has made it difficult for me to spread the word through my other web presences, and that ti why I&#x27;ve decide to own up. If you have any ideas about where it might be useful to spread the word, let me know by email.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested in GE, go to the site: &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.guiltyexpression.org&quot;&gt;www.guiltyexpression.org&lt;&#x2F;a&gt; and consider signing up for the mailing list to hear every now and then when a new article is published.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>RSS</title>
          <pubDate>Wed, 08 Jun 2005 15:22:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/08/rss-2/</link>
          <guid>https://artificialworlds.net/blog/2005/06/08/rss-2/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/08/rss-2/">&lt;p&gt;I really should provide an RSS feed. Does anyone know of a good reference on the simplest form of RSS that I can use to auto-generate some RSS from the HTML of this page? I&#x27;m prepared to do a little python coding to make it happen. Give me an email if so.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of giving me an email, wouldn&#x27;t it be great if I could have a proper blog that allowed comments? Well, I&#x27;d need a web host that allowed PHP or something, and that would cost money. If anyone knows of a good free hosting service (built on Free Software, preferably) let me know. I&#x27;ve registered on &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.advogato.org&#x2F;&quot;&gt;advogato.org&lt;&#x2F;a&gt; but although I have finally managed to work out how to post to a blog, it doesn&#x27;t allow comments, so the only benefit it offers is an RSS feed, and the lack of control is not worth that to me yet. I may get frustrated with my home-grown HTML&#x2F;FTP solution, but for the moment I&#x27;m quite enjoying it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>A real job</title>
          <pubDate>Tue, 07 Jun 2005 15:12:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/07/a-real-job/</link>
          <guid>https://artificialworlds.net/blog/2005/06/07/a-real-job/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/07/a-real-job/">&lt;p&gt;Getting used to working is an interesting thing. I haven&#x27;t been as tired as I thought I might be, probably partly because I got into a routine of getting up at 7am with my wife (she was working while I did my PhD). However, the weirdness of how much of a job is to do with social pressure is interesting. What I mean is that it takes a lot to get fired (in my job, anyway - I hope...) and you could probably get away with being pretty mediocre and keep your current salary. The things motivating you to do well and work hard are mainly desire for promotion (and within reason your salary will probably increase over time even if you&#x27;re mediocre) and social pressure. The main way in my work you are motivated is that if you don&#x27;t do what you were supposed to do one week, you have to survive a difficult moment on Monday when we have our status meeting.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, none of this is relevant to me because I am really motivated by how fun it is to be working with other people on something that I am reasonably good at, and also because I never have to read or write academic papers again.&lt;&#x2F;p&gt;
&lt;p&gt;Except, of course that I have my viva later this month, and I will have corrections out of that. But I&#x27;m trying not to think about it.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What is going on with FreeGuide?</title>
          <pubDate>Sat, 04 Jun 2005 23:12:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/04/what-is-going-on-with-freeguide/</link>
          <guid>https://artificialworlds.net/blog/2005/06/04/what-is-going-on-with-freeguide/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/04/what-is-going-on-with-freeguide/">&lt;p&gt;I thought I&#x27;d write a little bit about &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sf.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt;, since I&#x27;m planning to link this page off the FreeGuide page.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m the lead developer on FreeGuide, and having just started a new job (which so far is excellent) my free time is seriously limited, especially as my commute is 1.5 hours at the moment. I expect as I settle in I&#x27;ll find more time, and I also hope my commute may reduce in a few months. In the meantime I&#x27;ve had to fall back into maintenance mode, but fortunately there are several contributors stepping up to add long-requested features. The most prolific contributor has been Alex, who has been working on a number of excellent new features in our development branch. That branch started when Risto refactored the code into an MVC style and optimised the display to speed up massively operation with more than about 10 channels.&lt;&#x2F;p&gt;
&lt;p&gt;That branch has been around _far_ too long, as it&#x27;s a maintenance headache to look after 2 quite different branches, and it causes confusion when people send in patches against old code.&lt;&#x2F;p&gt;
&lt;p&gt;I really want to make the development branch reasonably bug-free and then declare it stable and retire the 0.8.x branch once and for all. I hope we can do that pretty soon.&lt;&#x2F;p&gt;
&lt;p&gt;Meanwhile, Alex is adding more features, the most notable of which is the plugins system. That will eventually allow you to download and install new listings grabbers without upgrading FreeGuide itself, and plug in loads of other things like exporters, recording schedulers, and loads more. Also excitingly soon to go in is the recording scheduler system developed by Reuel and integrated by Alex.&lt;&#x2F;p&gt;
&lt;p&gt;I want to make 0.10.x stable next release, then get those features in (probably under a 0.11.x name), stabilise them, go through the TODO and make a _short_ list of things to do before 1.0, do them and NOTHING else, and then finally release 1.0. It would be great if the plugin system turned out to be flexible enough to handle all the new thigns people suggest, but if not I plan to keep the 1.0.x branch stable long term, and allow further changes in a 1.1 branch.&lt;&#x2F;p&gt;
&lt;p&gt;The features I think are needed for 1.0 are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Recording (through a TV card) while FG is running&lt;&#x2F;li&gt;
&lt;li&gt;Scheduling recording&lt;&#x2F;li&gt;
&lt;li&gt;Coloured favourites&lt;&#x2F;li&gt;
&lt;li&gt;A manual and help&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hopefully not a lot else...&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Google</title>
          <pubDate>Fri, 03 Jun 2005 23:07:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/03/google/</link>
          <guid>https://artificialworlds.net/blog/2005/06/03/google/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/03/google/">&lt;p&gt;Can&#x27;t decide what to think about Google. I was very suspicious about gmail, and this whole &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;searchenginewatch.com&#x2F;searchday&#x2F;article.php&#x2F;3506541&quot;&gt;portal business&lt;&#x2F;a&gt; is very boring, and _so_ corporate, but you really can&#x27;t argue with &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;code.google.com&#x2F;summerofcode.html&quot;&gt;sponsoring open source contributions&lt;&#x2F;a&gt;, can you? If doing this kind of thing starts being seen as the way to impress the developer community (and therefore recruit the geekiest geeks) then everyone wins, and world domination is one step closer.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Beginning blog</title>
          <pubDate>Thu, 02 Jun 2005 15:23:00 +0000</pubDate>
          <author>Andy Balaam</author>
          <link>https://artificialworlds.net/blog/2005/06/02/beginning-blog/</link>
          <guid>https://artificialworlds.net/blog/2005/06/02/beginning-blog/</guid>
          <description xml:base="https://artificialworlds.net/blog/2005/06/02/beginning-blog/">&lt;p&gt;I&#x27;ve decided to start writing a blog, not least to convince people that although my work on open source projects (especially &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;freeguide-tv.sf.net&quot;&gt;FreeGuide&lt;&#x2F;a&gt;) has slowed down, it has not slipped out of my mind. Quite the opposite: I&#x27;m finding my lack of time very frustrating, especially when the train journey in to work every day gives me time to think of lots of excellent ideas, none of which I am going to be able to put into practice. I&#x27;ll log them here, and maybe someone else will do them. If not, I&#x27;ll do them when I retire.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m hoping to keep this blog as technical as possible, with only occasional daliances into personal matters, since it does my head in to be reading something like &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;planet.gnome.org&#x2F;&quot;&gt;Planet Gnome&lt;&#x2F;a&gt; and have to read about people&#x27;s cats being sick etc.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
