magnushoff.comAssorted stuff I've made
https://magnushoff.com/
enFri, 17 Apr 2020 00:00:00 +0000Magnus Hovland Hoffmaghoff@gmail.comHexagonal train gamehttps://magnushoff.com/blog/hexagonal-train-game/Fri, 17 Apr 2020 00:00:00 +0000<p>I'm dreaming of a train game inspired by <a href="https://www.openttd.org/">OpenTTD</a>, a game I have spent the
typical "too many hours" playing. I obviously enjoy it a lot, but there are
nevertheless some annoyances that would be nice to fix. So I have spent a lot
of time designing a new game in my mind that fixes everything and is so much
better. Maybe. The only way to know is to play the game, and that requires
having the game to play.</p>
<img src="/assets/traingame/sketch-800w.jpg" srcset="/assets/traingame/sketch-800w.jpg 800w, /assets/traingame/sketch-1600w.jpg 1600w, /assets/traingame/sketch-2400w.jpg 2400w" style="width: 100%; max-width: 800px">
<p>One of the things I have been wondering about is if it might be nicer to play
on an hexagonal grid rather than the square grid in OpenTTD. On the square grid,
you can build rails that go along the grid or diagonally on it, but there are
annoying differences in what's possible with the different directions. I have
sketched on <a href="https://incompetech.com/graphpaper/hexagonal/">hexagonal grid paper</a> and it looks interesting, so I have
proceeded to create an interactive prototype to test what editing it in the game
might be like.</p>
<style>
.hex-background {
fill: antiquewhite;
stroke: none;
}
.hex-foreground {
fill: none;
stroke: #ccc;
stroke-width: 1px;
stroke-linejoin: round;
}
.hex-edge {
fill: none;
stroke: #e4e4e4;
stroke-width: 1px;
}
.hex-edge.highlight {
fill: salmon;
}
.rails {
fill: none;
stroke: #333;
stroke-width: 5;
}
.rails.ghost {
stroke: #ccf;
}
.svg-container {
width: 100%;
overflow: hidden;
position: relative;
}
.rail-canvas {
position: absolute;
top: 0;
}
#yew {
height: 370px;
}
#yew>.rail-canvas {
left: calc(50% - 165px);
}
</style>
<p>Pretty soon, I bumped into the linked questions of how to represent this, and
which user interaction to offer for the editing. My initial analysis was the
following: There are three ways to build a straight rail across a cell, and
there are six ways to build a bend in a cell. Therefore each cell needs nine
bits to represent all possible combinations of rail types.</p>
<div class="svg-container" style="height: 56px">
<svg class="rail-canvas" viewBox="0 0 624 56" width=624 height=56 style="left: calc(50% - 312px)">
<g transform="translate(32,28)">
<g transform="translate(0,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y2="4.3397465" x2="-27.51666" x1="17.51666" y1="-21.660254"></line><line class="rails" y2="21.660254" x1="27.51666" x2="-17.51666" y1="-4.3397465"></line></g>
<g class="" transform="translate(70,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y2="21.660254" x2="17.51666" x1="-27.51666" y1="-4.3397465"></line><line class="rails" y2="4.3397465" x1="-17.51666" x2="27.51666" y1="-21.660254"></line></g>
<g class="" transform="translate(140,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y2="26" y1="-26" x2="-10" x1="-10"></line><line class="rails" y2="26" x1="10" x2="10" y1="-26"></line></g>
<g class="" transform="translate(210,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M-27.51666,-4.3397465 A55,55 0 0 0 27.51666,-4.3397465 M-17.51666,-21.660254 A35,35 0 0 0 17.51666,-21.660254"></path></g>
<g class="" transform="translate(280,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M-10,-26 A55,55 0 0 0 17.51666,21.660254 M10,-26 A35,35 0 0 0 27.51666,4.3397465"></path></g>
<g class="" transform="translate(350,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M17.51666,-21.660254 A55,55 0 0 0 -10,26 M27.51666,-4.3397465 A35,35 0 0 0 10,26"></path></g>
<g class="" transform="translate(420,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M27.51666,4.3397465 A55,55 0 0 0 -27.51666,4.3397465 M17.51666,21.660254 A35,35 0 0 0 -17.51666,21.660254"></path></g>
<g class="" transform="translate(490,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M10,26 A55,55 0 0 0 -17.51666,-21.660254 M-10,26 A35,35 0 0 0 -27.51666,-4.3397465"></path></g>
<g class="" transform="translate(560,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><path class="rails" d="M-17.51666,21.660254 A55,55 0 0 0 10,-26 M-27.51666,4.3397465 A35,35 0 0 0 -10,-26"></path></g>
</g>
</svg>
</div>
<p>Most, but maybe not all, combinations can make some kind of sense in the correct
context.</p>
<div class="svg-container" style="height: 56px">
<svg class="rail-canvas" viewBox="0 0 204 56" width=204 height=56 style="left: calc(50% - 102px)">
<g transform="translate(32,28)">
<g class="" transform="translate(0,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon>
<path class="rails" d="M-17.51666,21.660254 A55,55 0 0 0 10,-26 M-27.51666,4.3397465 A35,35 0 0 0 -10,-26"></path><path class="rails" d="M17.51666,-21.660254 A55,55 0 0 0 -10,26 M27.51666,-4.3397465 A35,35 0 0 0 10,26"></path>
<path class="rails" d="M17.51666,-21.660254 A55,55 0 0 0 -10,26 M27.51666,-4.3397465 A35,35 0 0 0 10,26"></path></g>
<g class="" transform="translate(70,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon>
<line class="rails" y2="21.660254" x2="17.51666" x1="-27.51666" y1="-4.3397465"></line><line class="rails" y2="4.3397465" x1="-17.51666" x2="27.51666" y1="-21.660254"></line>
<path class="rails" d="M17.51666,-21.660254 A55,55 0 0 0 -10,26 M27.51666,-4.3397465 A35,35 0 0 0 10,26"></path>
</g>
<g class="" transform="translate(140,0)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon>
<path class="rails" d="M10,26 A55,55 0 0 0 -17.51666,-21.660254 M-10,26 A35,35 0 0 0 -27.51666,-4.3397465"></path>
<path class="rails" d="M27.51666,4.3397465 A55,55 0 0 0 -27.51666,4.3397465 M17.51666,21.660254 A35,35 0 0 0 -17.51666,21.660254"></path>
<path class="rails" d="M17.51666,-21.660254 A55,55 0 0 0 -10,26 M27.51666,-4.3397465 A35,35 0 0 0 10,26"></path>
</g>
</g>
</svg>
</div>
<p>But the question of context got me thinking. I have made many mistakes in
OpenTTD where I leave a discontinuity in a railway in a place that's crowded
enough that it is hard to spot the error. This typically happens in an
intersection with many connections, and I have missed one of the multiple
valid connections.</p>
<div class="svg-container" style="height: 158px">
<svg class="rail-canvas" viewBox="0 0 204 158" width=204 height=158 style="left: calc(50% - 102px)">
<g transform="translate(102,79)">
<g class="" transform="translate(-45,-26)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon></g>
<g class="" transform="translate(-45,26)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y1="-21.660254" x2="-27.51666" x1="17.51666" y2="4.3397465"></line><line class="rails" x1="27.51666" x2="-17.51666" y1="-4.3397465" y2="21.660254"></line></g>
<g class="" transform="translate(0,-52)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon></g>
<g class="" transform="translate(0,0)">
<polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon>
<polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon>
<polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon>
<polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon>
<polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon>
<polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon>
<polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon>
<polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon>
<line class="rails" x2="-27.51666" y2="4.3397465" x1="17.51666" y1="-21.660254"></line>
<line class="rails" x1="27.51666" y2="21.660254" y1="-4.3397465" x2="-17.51666"></line>
</g>
<g class="" transform="translate(0,52)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon></g>
<g class="" transform="translate(45,-26)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y2="4.3397465" x1="17.51666" x2="-27.51666" y1="-21.660254"></line><line class="rails" y1="-4.3397465" x2="-17.51666" x1="27.51666" y2="21.660254"></line></g>
<g class="" transform="translate(45,26)"><polygon class="hex-background" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><polygon class="hex-edge" points="-18,0 -30,0 -15,-26 -9,-15.6"></polygon><polygon class="hex-edge" points="-9,-15.6 -15,-26 15,-26 9,-15.6"></polygon><polygon class="hex-edge" points="9,-15.6 15,-26 30,0 18,0"></polygon><polygon class="hex-edge" points="18,0 30,0 15,26 9,15.6"></polygon><polygon class="hex-edge" points="9,15.6 15,26 -15,26 -9,15.6"></polygon><polygon class="hex-edge" points="-9,15.6 -15,26 -30,0 -18,0"></polygon><polygon class="hex-foreground" points="-15,-26 15,-26 30,0 15,26 -15,26 -30,0"></polygon><line class="rails" y2="21.660254" x1="-27.51666" x2="17.51666" y1="-4.3397465"></line><line class="rails" x2="27.51666" x1="-17.51666" y2="4.3397465" y1="-21.660254"></line></g>
</g>
</svg>
</div>
<p>This suggests focusing on connections between cells instead of stretches of rail
inside each cell. Or edges instead of nodes, in graph parlance. You would then
mark each edge as either connected or disconnected, and each cell would then
include the rails to complete all pairs of connected edges.</p>
<p>The representation of this would require one bit per edge. For the six-sided
hexagons in this game, that sounds like six bits per cell, but each edge is of
course shared between two cells, making the entropy per cell three bits. This
sharing is what makes this scheme appealing: It guarantees a certain coherence
for adjacent cells.</p>
<p>If my combinatorics is any good, this leaves us with a lot fewer possibilities
for constructing our rails. This can be good or bad. Does it limit the player's
expressiveness or creativity? Does it eliminate error states? And what does it
do to the user interface?</p>
<p>In Transport Tycoon Deluxe, the game OpenTTD is a remake of, you only have crude
tools available to build rails. You must select from a toolbar which direction
of railway you want to build, and then click on the map. For other directions,
you have to pick a different tool. To remove a railway you have to activate the
bulldozer tool <em>and</em> pick the direction. This is tiresome. For editing boolean
edges between cells, it is sufficient to be able
to toggle the state of each edge. This one tool takes care of all directions as
well as removal. There is of course also room for higher level tools to build
longer stretches of rails.</p>
<p>Let's try it! Tap or click edges on the map below to create railways:</p>
<script src="pkg/bundle.js" defer></script>
<div class="svg-container" id="yew">(JavaScript must be enabled for the interactive map)</div>
<p>I have already had some time to experiment with this, and I am very pleased with
how it turned out. I enjoy building rails this way. However, I also have some
concerns and already some rough ideas for how to counter those concerns. That
will be something to write about another time.</p>
Writing dependency-free JavaScripthttps://magnushoff.com/blog/dependency-free-javascript/Sat, 11 Jan 2020 00:00:00 +0000<p>Lately, I have been working on a web application, <a href="https://github.com/maghoff/pixu.rs/tree/master/js">pixu.rs</a><sup class="footnote-reference"><a href="#1">1</a></sup>, that,
while small, includes an interactive form with image uploading and multiple
phases. This is exactly the kind of thing I tend to turn into a mess when I'm
not using React, Redux and everything that comes with that ecosystem, but small enough
that I hesitate to bring in the big guns. For pixu.rs, I have experimented
with applying learnings from the React ecosystem to my code without actually
using any dependencies, and in the process I have discovered a few techniques
I thought were valuable enough to write down.</p>
<p>I will not motivate the decision to forgo dependencies in this post. I will,
however, note that if you have the motivation, you must be prepared to make
some concessions. The biggest and most obvious one is that you lose the
immediate mode rendering React implements. These are my tips for making the
best of it.</p>
<h2 id="tip-1-create-the-dom-up-front">Tip #1: Create the DOM up front</h2>
<p>React is excellent for getting control of your dynamically generated HTML.
Doing without React means letting go of this. Instead, I have had good
experience with generating all the HTML I need up front on the server and then using
JavaScript to show and hide different parts of the document as needed. This
works well for the appropriate kinds of dynamic HTML, where the user sees
different "pages" at different times and the set of possible states is
statically known at the outset.</p>
<p>For content that must be generated dynamically, you can still create a
template of it in your HTML document. Hide it with CSS and then use
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode"><code>Node.cloneNode()</code></a> to instantiate it. This lets you off with only very few
DOM API calls to generate HTML.</p>
<p><strong>Update:</strong> Since posting this, I have been notified that this is a well-trodden
path that has a few tricky corner cases. As alternatives to including the
template directly in your HTML, there is also the trick of including it in a
<code><script type="text/html"></code>-tag, instantiating it via <code>innerHTML</code> and the more
recent <a href="https://javascript.info/template-element"><code><template></code> element</a>, both of which are responses to the corner cases,
I believe.</p>
<h2 id="tip-2-centralize-rendering">Tip #2: Centralize rendering</h2>
<p>Take inspiration from React and centralize all the rendering code to one
function, <code>render(...)</code>. Letting go of immediate mode rendering means we have to
look at the <em>changes</em> in state to see which <em>changes</em> we have to do to the DOM.
To facilitate this, our rendering function will have two parameters: <code>prevState</code>
and <code>nextState</code>. Structure the <code>render</code> function in blocks that test for changes
in the state and make the relevant updates to the DOM:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">render</span><span style="color:#323232;">(prevState, nextState) {
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(prevState.count </span><span style="font-weight:bold;color:#a71d5d;">!= </span><span style="color:#323232;">nextState.count) {
</span><span style="color:#0086b3;">document</span><span style="color:#323232;">.querySelector(</span><span style="color:#183691;">'.counter--display'</span><span style="color:#323232;">).textContent </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">nextState.count;
}
</span><span style="font-style:italic;color:#969896;">// And so on...
</span><span style="color:#323232;">}
</span></code></pre>
<p>This is far from as nice as React rendering code, but it is nicer than a
sprawling mess of unstructured DOM updates and it is fully possible to maintain.</p>
<p>Add <code>setState(...)</code> and <code>updateState(...)</code> so you don't have to call <code>render</code>
directly:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">state;
</span><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">setState</span><span style="color:#323232;">(nextState) {
render(state, nextState);
state </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">nextState;
}
</span><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">updateState</span><span style="color:#323232;">(delta) {
</span><span style="font-weight:bold;color:#a71d5d;">const </span><span style="color:#323232;">nextState </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">{ </span><span style="font-weight:bold;color:#a71d5d;">...</span><span style="color:#323232;">state, </span><span style="font-weight:bold;color:#a71d5d;">...</span><span style="color:#323232;">delta };
setState(nextState);
}
</span></code></pre>
<p>This simple implementation of <code>updateState</code> means your state object cannot be
nested. You may have to update your naming scheme to fit this, but it works
<em>just fine</em> in practice. Alternatively, you can have a more elaborate
<code>updateState</code> function. But isn't there beauty in keeping it this simple?</p>
<h2 id="tip-3-keep-the-compile-step">Tip #3: Keep the compile step</h2>
<p>I started out writing my JavaScript to run in the browser directly, without
either compiling or minifying. After a while, it turned out that I had
inadvertently broken compatibility with one of my target platforms. At this
point I was faced with the choice of either debugging it on somebody else's
device without developer tools… or simply throwing Babel at it.</p>
<p>In the end I decided that making use of Webpack and Babel would do me more good
than harm and let me keep most of the advantages of trying to go
dependency-free. I like to think that these tools are more easily replaceable
than library dependencies: the interface is very high level (JS→JS) and I would
not have to rewrite any code if I were to swap these out for equivalents.</p>
<p>I have been very happy with this decision.</p>
<h2 id="tip-4-the-dom-object">Tip #4: The DOM object</h2>
<p>When you render all the HTML up front, you have to find the DOM objects all the
time to manipulate them. Centralize this to one constant that you fill in at the
beginning to make it easier to read and write as well as improving execution
speed:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">const </span><span style="color:#0086b3;">DOM </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">{
imageInput: </span><span style="color:#0086b3;">document</span><span style="color:#323232;">.getElementById(</span><span style="color:#183691;">'image'</span><span style="color:#323232;">),
preview: </span><span style="color:#0086b3;">document</span><span style="color:#323232;">.querySelector(</span><span style="color:#183691;">'.preview'</span><span style="color:#323232;">),
</span><span style="font-style:italic;color:#969896;">// ...
</span><span style="color:#323232;">};
</span></code></pre>
<p>Unlike the <code>state</code> object, the <code>DOM</code> object may well be nested, to reflect the
structure of your application.</p>
<h2 id="tip-5-keep-state-in-the-dom">Tip #5: Keep state in the DOM</h2>
<p>T'ain't no sin to <!-- take off your skin and dance around in your bones --> let
the canonical storage for a text input be the input DOM
element. The trick is to be able to identify when it is time to move it into the
state object, like you are supposed to do with React.</p>
<p>Storing the value in the DOM element is sufficient when you only read from it in
response to other user actions, such as clicking a submit button. Storing the
value in the state object is a good idea when the UI is responding interactively
to it, for example by updating a text field or performing form validation.</p>
<p>Keeping the state in the DOM element, when possible, saves you a lot of boiler
plate code.</p>
<h2 id="tip-6-cheat">Tip #6: Cheat</h2>
<p>Know and love <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/alert"><code>alert</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm"><code>confirm</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt"><code>prompt</code></a>!</p>
<p>These are excellent tools for prototyping, but you might even consider them for
your finished application if you only use them on an administration page or some
other part with a very limited audience.</p>
<p>If you put them in, and you later want to replace them with modal dialog boxes
implemented in HTML and JS, you can get far with an async implementation. Update
your state to show the dialog and return a <code>Promise</code> which resolves with the
proper user interaction. Voilà!</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">asyncAlert</span><span style="color:#323232;">(message) {
updateState({
alert: message,
});
</span><span style="font-weight:bold;color:#a71d5d;">return new </span><span style="color:#0086b3;">Promise</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">function </span><span style="color:#323232;">(resolve) {
</span><span style="color:#0086b3;">DOM</span><span style="color:#323232;">.</span><span style="color:#0086b3;">alertOkButton</span><span style="color:#323232;">.</span><span style="font-weight:bold;color:#323232;">onclick </span><span style="font-weight:bold;color:#a71d5d;">= function </span><span style="color:#323232;">() {
updateState({
alert: </span><span style="color:#0086b3;">null</span><span style="color:#323232;">,
});
resolve();
};
});
}
</span></code></pre><h2 id="tip-7-redux">Tip #7: Redux</h2>
<p>So what about Redux? I haven't gotten as far as taking much inspiration from
Redux, so calling this a tip is taking a liberty. But it also brings up a
point I like about this dependency-free experiment: You don't have to build a
framework-replica up front. Refactor as you see the need for better things.</p>
<p>For Redux-inspiration, I'm pretty sure you can get far with writing ad hoc
reducers along with a simple <code>dispatch</code> implementation:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">rootReducer</span><span style="color:#323232;">(state, action) {
</span><span style="font-weight:bold;color:#a71d5d;">return </span><span style="color:#323232;">{
subsystem1: subsystem1Reducer(state.subsystem1, action),
subsystem2: subsystem2Reducer(state.subsystem2, action),
</span><span style="font-style:italic;color:#969896;">// and so on...
</span><span style="color:#323232;">};
}
</span><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">dispatch</span><span style="color:#323232;">(action) {
</span><span style="font-weight:bold;color:#a71d5d;">let </span><span style="color:#323232;">newState </span><span style="font-weight:bold;color:#a71d5d;">= </span><span style="color:#323232;">rootReducer(state, action);
setState(newState);
}
</span></code></pre>
<p>This should allow you to have a structured state object as well!</p>
<h2 id="conclusion">Conclusion</h2>
<p>And that's it! Those are my concrete experiences (plus one imaginary one) with
writing JavaScript without library dependencies:</p>
<ol>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-1-create-the-dom-up-front">Create the DOM up front</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-2-centralize-rendering">Centralize rendering</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-3-keep-the-compile-step">Keep the compile step</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-4-the-dom-object">The DOM object</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-5-keep-state-in-the-dom">Keep state in the DOM</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-6-cheat">Cheat</a></li>
<li><a href="https://magnushoff.com/blog/dependency-free-javascript/#tip-7-redux">Redux</a></li>
</ol>
<p>Now, you go and write dependency-free JavaScript <em>today!</em> I mean, if you want
to. Or not. But if you ever get the urge, maybe this can be a good starting
point.</p>
<hr />
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
<p>You are supposed to pronounce it like "pictures". The trailing ".rs"
signifies that it is written in Rust. Yes, it is a <em>very</em> clever name.</p>
</div>
Site maintenancehttps://magnushoff.com/blog/site-maintenance/Tue, 08 Oct 2019 00:00:00 +0000<p>Over the last weeks I have implemented various improvements to this
site. I have been saving them for a single write-up, because I want to keep the
volume of <a href="/tags/meta">meta-posts</a> low, lest this become another blog about blogging and
how I would want to write more <em>next year.</em></p>
<p>First up is tags. Some blog posts get one or more tags, and I can now give you
an overview of all my posts about <a href="/tags/doom">doom</a>, for example.</p>
<p>I did this first to pave the way for another thing I wanted to do. I have now
reposted my x86-64 assembly tutorial <a href="https://magnushoff.com/articles/asmtut/">Asmtut</a> on this blog, each entry
back-dated to its original posting date. This tutorial used to be hosted on
Google+, which was never more than a ghost town that is now simply gone.</p>
<p>I have removed <a href="/blog/pnacl/">"Actually getting started with Portable Native Client"</a>
from the <a href="/">front page</a> and reposted it as a blog post. I thought the technology
looked fun and promising, and I wanted to keep up with it. Now, it is as dead
as Google+.
While blog posts can go out of date and be kept for posterity, this is not
what I want the fetaured articles on my front page to be like. Besides, the
front page layout works a lot better with six articles instead of seven 😉</p>
<p><a href="/snygg/">Snygg</a> uses PNaCl and no longer runs even in Chrome, but I have decided not
to remove it. Instead, I will reimplement the game in Rust and compile that to
WASM. That will be my third implementation after the first in Python and the
second in C++. The rest of that page also needs a refresh. I don't want to
wait until everything is ready, so I'm adding a development blog to the
Snygg-page and I will publish as I go.</p>
<p>The <a href="/plaintalk/introduction-and-definition.html">PlainTalk</a> page deserves some love. I wrote it in 2013, imagining I would
build a small site around it. I have never gotten around to that. For this
update I have only changed the footer, which used to have links to my presence
on Google+ and Bitbucket 😂 I created it before I had this site, but now I do,
so now it just links to the front page.</p>
<p>The rest of the article pages have been updated to be built using the same
<a href="https://www.getzola.org/">static site generator</a> I use for the blog. They now all have the same general
layout, including header and footer, and several inconsistencies have been
removed. This makes it easier to improve and update the whole site in one go
and it allows me to create more advanced relations between pages, such as
showing blog posts for a given tag on a page.</p>
<p>Behind the scenes I have also migrated version control system <a href="https://magnushoff.com/blog/kick-the-bitbucket/">from Mercurial
to Git</a>, prompted by Bitbucket's descision to terminate Mercurial support. Oh,
and I have dropped the Bitbucket link from the footer.</p>
<p>Put together this refreshes my site so it's a lot more current, and better fit
for interesting updates in the future. Man, this thing is work.</p>
<p>A final meta-comment on this meta-post: I wrote this text before starting on
the work it describes, imagining what I would want to do. I have of course had
to adjust it as I went, but it was a good way to get started. I had a hard
time motivating myself for each individual task because of their relative
small impact, but collecting it all in this post made it easier to look at and
think "Yes! This is where I want to be!"</p>
Kick the Bitbuckethttps://magnushoff.com/blog/kick-the-bitbucket/Mon, 30 Sep 2019 00:00:00 +0000<p>On June 1, Atlassian will indiscriminately delete all Mercurial repositories
from Bitbucket, becoming another Git-only site. In the beginning,
Bitbucket exclusively supported Mercurial, so this change is unexpected to say
the least.</p>
<p>In addition to supporting Mercurial, the main distinguishing feature that kept
me using Bitbucket was its generous offer of unlimited private repositories
for free. After Microsoft acquired GitHub in late 2018, it didn't take long
for GitHub to <a href="https://github.blog/2019-01-07-new-year-new-github/">start offering</a> this as well. Faced with this competition, I
guess Atlassian figured out it would only make sense to remove their last
remaining distinguishing feature.</p>
<p>To add insult to injury, they have really made zero effort in assisting with
migration, putting the entire burden on their users. I'm not a paying
customer, but there's no indication that they are treating them any
differently either. In <a href="https://bitbucket.org/blog/sunsetting-mercurial-support-in-bitbucket">their own words</a>:</p>
<blockquote>
<p>We realize that there is no one-size-fits-all solution. That’s why we’ve
created the following resources to best equip you with the knowledge and
tools for a seamless transition:</p>
<ul>
<li>A Community thread to discuss conversion tools, migration, tips, and offer
troubleshooting help</li>
<li>A Git tutorial that covers anywhere from the basics of creating pull
requests to rebasing and Git hooks</li>
</ul>
</blockquote>
<p>They have created a fricken forum post! The absolute minimal acceptable effort
here would be to create a migration tool that let you switch SCMs in-place, so
issues, wikis, hooks and so on would keep on working. This is now all an
exercise for the reader.</p>
<p>That being said, I have really been looking for a good way off Bitbucket ever
since it was acquired by Atlassian in 2010. I never trusted them to do
anything good for the service, and I have felt it getting worse slowly, but
surely, all that time. This latest move by Atlassian really cements my
distrust of the company, and it is the push I needed to finally move away.</p>
<h2 id="moving-away">Moving away</h2>
<p>So I made a script. The ones I found were old enough to be based on obsolete
APIs and they focused on migrating <em>one</em> repository. My goal was to migrate
all my stuff from Bitbucket over to GitHub.</p>
<p>The script iterates through all repositories you own on Bitbucket and for each
one:</p>
<ol>
<li>Creates a corresponding repository on GitHub with the same name. If the
name is already taken, the script skips to the next repository in the
list.</li>
<li>Migrates the source code with <code>git --mirror</code>. If the source repository is
Mercurial, conversion is performed with <a href="https://github.com/felipec/git-remote-hg">git-remote-hg</a>.</li>
<li>If there is an associated wiki, it is migrated as well. Weirdly, you
cannot push to the wiki of a GitHub repository before creating the first
page via the web interface. I implemented a work-around that requires you
to click a link and a button before the wiki is migrated.</li>
<li>Optionally the source repository is deleted, leaving a UI level redirect
to the new GitHub repository.</li>
</ol>
<p>Note that the script does not migrate issues, downloads, hooks and probably
more things. This was sufficient for my needs. I do hope others will pick up
the torch and implement more features as they see the need.</p>
<p>I have reproduced the script below. You can also download it directly:
<a href="/escape-bitbucket.sh">escape-bitbucket.sh</a> or go to my <a href="https://gist.github.com/maghoff/2b86e4ca3208a5ce443b35c5860c8a4d">gist</a>. Feel free to fork the gist to add
your own features!</p>
<p>Happy exodus!</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">#!/usr/bin/env bash
# Originally published on https://magnushoff.com/blog/kick-the-bitbucket/
# Copyright (c) 2019 Magnus Hovland Hoff
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
</span><span style="color:#62a35c;">set</span><span style="color:#323232;"> -eo pipefail
</span><span style="font-style:italic;color:#969896;"># Check for installed dependencies
</span><span style="color:#323232;">for CMD in curl git hg git-remote-hg jq
</span><span style="font-weight:bold;color:#a71d5d;">do
if ! </span><span style="color:#323232;">which </span><span style="color:#183691;">"$</span><span style="color:#323232;">CMD</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">></span><span style="color:#323232;"> /dev/null
</span><span style="font-weight:bold;color:#a71d5d;">then
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"\e[1m\e[31mError: Missing command $</span><span style="color:#323232;">CMD</span><span style="color:#183691;">\e[0m"
</span><span style="color:#62a35c;">exit</span><span style="color:#323232;"> 1
</span><span style="font-weight:bold;color:#a71d5d;">fi
done
</span><span style="font-style:italic;color:#969896;"># Specify -1 on the command line to only migrate one repository
</span><span style="color:#323232;">ONE_REPO=false
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#62a35c;">[ </span><span style="color:#183691;">"x$</span><span style="color:#323232;">1</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">== </span><span style="color:#183691;">"x-1" </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">then
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"\e[1m\e[32m-1 specified. Will only migrate one repository\e[0m"
</span><span style="color:#323232;">ONE_REPO</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">true
</span><span style="font-weight:bold;color:#a71d5d;">fi
if </span><span style="color:#62a35c;">[ </span><span style="color:#323232;">-z </span><span style="color:#183691;">"$</span><span style="color:#323232;">BB_USERNAME</span><span style="color:#183691;">" </span><span style="color:#62a35c;">] </span><span style="font-weight:bold;color:#a71d5d;">; then </span><span style="color:#62a35c;">read </span><span style="color:#323232;">-p </span><span style="color:#183691;">"Bitbucket username: "</span><span style="color:#323232;"> BB_USERNAME</span><span style="font-weight:bold;color:#a71d5d;">; fi
if </span><span style="color:#62a35c;">[ </span><span style="color:#323232;">-z </span><span style="color:#183691;">"$</span><span style="color:#323232;">BB_PASSWORD</span><span style="color:#183691;">" </span><span style="color:#62a35c;">] </span><span style="font-weight:bold;color:#a71d5d;">; then </span><span style="color:#62a35c;">read</span><span style="color:#323232;"> -sp </span><span style="color:#183691;">"Bitbucket password: "</span><span style="color:#323232;"> BB_PASSWORD</span><span style="font-weight:bold;color:#a71d5d;">; </span><span style="color:#62a35c;">echo</span><span style="font-weight:bold;color:#a71d5d;">; fi
if </span><span style="color:#62a35c;">[ </span><span style="color:#323232;">-z </span><span style="color:#183691;">"$</span><span style="color:#323232;">GH_USERNAME</span><span style="color:#183691;">" </span><span style="color:#62a35c;">] </span><span style="font-weight:bold;color:#a71d5d;">; then </span><span style="color:#62a35c;">read </span><span style="color:#323232;">-p </span><span style="color:#183691;">"GitHub username: "</span><span style="color:#323232;"> GH_USERNAME</span><span style="font-weight:bold;color:#a71d5d;">; fi
if </span><span style="color:#62a35c;">[ </span><span style="color:#323232;">-z </span><span style="color:#183691;">"$</span><span style="color:#323232;">GH_PASSWORD</span><span style="color:#183691;">" </span><span style="color:#62a35c;">] </span><span style="font-weight:bold;color:#a71d5d;">; then </span><span style="color:#62a35c;">read</span><span style="color:#323232;"> -sp </span><span style="color:#183691;">"GitHub password: "</span><span style="color:#323232;"> GH_PASSWORD</span><span style="font-weight:bold;color:#a71d5d;">; </span><span style="color:#62a35c;">echo</span><span style="font-weight:bold;color:#a71d5d;">; fi
</span><span style="color:#323232;">BB_CURL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"curl -s --user $</span><span style="color:#323232;">BB_USERNAME</span><span style="color:#183691;">:$</span><span style="color:#323232;">BB_PASSWORD</span><span style="color:#183691;">"
</span><span style="color:#323232;">GH_CURL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"curl -s --user $</span><span style="color:#323232;">GH_USERNAME</span><span style="color:#183691;">:$</span><span style="color:#323232;">GH_PASSWORD</span><span style="color:#183691;"> -H Accept:application/vnd.github.v3+json"
</span><span style="color:#62a35c;">echo
echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"This script will migrate the \e[36msource\e[0m code and \e[36mwikis\e[0m from Bitbucket to GitHub."
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"NOTE: Nothing else will be migrated! \e[33mIssues\e[0m, \e[33mdownloads\e[0m and more will be lost."
</span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#323232;">true</span><span style="font-weight:bold;color:#a71d5d;">; do
</span><span style="color:#62a35c;">read </span><span style="color:#323232;">-p </span><span style="color:#183691;">"Delete repos from Bitbucket after successful migration (yes/no)? "</span><span style="color:#323232;"> DELETE
</span><span style="font-weight:bold;color:#a71d5d;">case </span><span style="color:#183691;">"$</span><span style="color:#323232;">DELETE</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">in
</span><span style="color:#323232;">yes</span><span style="font-weight:bold;color:#a71d5d;">) break</span><span style="color:#323232;">;;
no</span><span style="font-weight:bold;color:#a71d5d;">) break</span><span style="color:#323232;">;;
</span><span style="font-weight:bold;color:#a71d5d;">*) </span><span style="color:#62a35c;">echo </span><span style="color:#183691;">"Please answer yes or no"</span><span style="color:#323232;">;;
</span><span style="font-weight:bold;color:#a71d5d;">esac
done
</span><span style="color:#323232;">TMP</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">$(</span><span style="color:#323232;">mktemp -d </span><span style="color:#183691;">"${</span><span style="color:#323232;">TMPDIR</span><span style="font-weight:bold;color:#a71d5d;">:-</span><span style="color:#183691;">/tmp/}$(</span><span style="color:#323232;">basename </span><span style="color:#183691;">$</span><span style="color:#323232;">0</span><span style="color:#183691;">).XXXXXXXXXXXX")
</span><span style="font-weight:bold;color:#a71d5d;">function </span><span style="font-weight:bold;color:#795da3;">cleanup </span><span style="color:#323232;">{
rm -rf </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">"
</span><span style="color:#323232;">}
</span><span style="color:#62a35c;">trap</span><span style="color:#323232;"> cleanup EXIT
</span><span style="font-style:italic;color:#969896;"># Prefetch all the data from Bitbucket to avoid problems with deleting while iterating
</span><span style="color:#323232;">echo -en </span><span style="color:#183691;">"\e[1m\e[32mFetching metadata from Bitbucket"
</span><span style="color:#323232;">ALL_REPOS</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/all-repos.json"
</span><span style="color:#62a35c;">echo </span><span style="font-weight:bold;color:#a71d5d;">> </span><span style="color:#183691;">"$</span><span style="color:#323232;">ALL_REPOS</span><span style="color:#183691;">"
</span><span style="color:#323232;">PAGE</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"https://api.bitbucket.org/2.0/repositories/$</span><span style="color:#323232;">BB_USERNAME</span><span style="color:#183691;">"
</span><span style="font-weight:bold;color:#a71d5d;">while </span><span style="color:#62a35c;">[ </span><span style="color:#183691;">"x$</span><span style="color:#323232;">PAGE</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">!=</span><span style="color:#323232;"> xnull </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">do
</span><span style="color:#323232;">$BB_CURL </span><span style="color:#183691;">"$</span><span style="color:#323232;">PAGE</span><span style="color:#183691;">"</span><span style="color:#323232;"> -o </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/page.json"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-n </span><span style="color:#183691;">"."
</span><span style="color:#323232;">jq -c </span><span style="color:#183691;">'.values[]' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/page.json" </span><span style="font-weight:bold;color:#a71d5d;">>> </span><span style="color:#183691;">"$</span><span style="color:#323232;">ALL_REPOS</span><span style="color:#183691;">"
</span><span style="color:#323232;">PAGE</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">$(</span><span style="color:#323232;">jq -r</span><span style="color:#183691;"> .next "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/page.json")
</span><span style="font-weight:bold;color:#a71d5d;">done
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"\e[0m"
</span><span style="color:#323232;">SIZE</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.size' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/page.json")"
</span><span style="color:#323232;">I</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">0
</span><span style="font-style:italic;color:#969896;"># jq iteration due to https://starkandwayne.com/blog/bash-for-loop-over-json-array-using-jq/
</span><span style="font-weight:bold;color:#a71d5d;">for</span><span style="color:#323232;"> REPO </span><span style="font-weight:bold;color:#a71d5d;">in </span><span style="color:#323232;">$(jq -r </span><span style="color:#183691;">'. | @base64' "$</span><span style="color:#323232;">ALL_REPOS</span><span style="color:#183691;">"</span><span style="color:#323232;">)
</span><span style="font-weight:bold;color:#a71d5d;">do
</span><span style="color:#62a35c;">echo </span><span style="color:#183691;">"$</span><span style="color:#323232;">REPO</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">| </span><span style="color:#323232;">base64 --decode </span><span style="font-weight:bold;color:#a71d5d;">> </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo"
</span><span style="color:#323232;">I</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">$(( I </span><span style="font-weight:bold;color:#a71d5d;">+ </span><span style="color:#0086b3;">1 </span><span style="color:#183691;">))
</span><span style="color:#323232;">SELF</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.links.self.href' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo")"
</span><span style="color:#323232;">CLONE_URL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.links.clone[] | select(.name == "ssh") | .href' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo")"
</span><span style="color:#323232;">SCM</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.scm' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo")"
</span><span style="color:#323232;">SLUG</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.slug' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo")"
</span><span style="color:#323232;">HAS_WIKI</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r </span><span style="color:#183691;">'.has_wiki' "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo")"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">"\e[1m\e[32mMigrating \e[36m$</span><span style="color:#323232;">SLUG</span><span style="color:#183691;">\e[32m ($</span><span style="color:#323232;">I</span><span style="color:#183691;">/$</span><span style="color:#323232;">SIZE</span><span style="color:#183691;">)\e[0m"
</span><span style="font-weight:bold;color:#a71d5d;">case </span><span style="color:#183691;">"$</span><span style="color:#323232;">SCM</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">in
</span><span style="color:#323232;">git</span><span style="font-weight:bold;color:#a71d5d;">)
</span><span style="color:#323232;">CLONE_ARG</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$</span><span style="color:#323232;">CLONE_URL</span><span style="color:#183691;">"
</span><span style="color:#323232;">;;
hg</span><span style="font-weight:bold;color:#a71d5d;">)
</span><span style="color:#323232;">CLONE_ARG</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"hg::$</span><span style="color:#323232;">CLONE_URL</span><span style="color:#183691;">"
</span><span style="color:#323232;">;;
</span><span style="font-weight:bold;color:#a71d5d;">*)
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[31mAborting due to unknown SCM: \e[36m$</span><span style="color:#323232;">SCM</span><span style="color:#183691;">\e[0m" </span><span style="font-weight:bold;color:#a71d5d;">>&</span><span style="color:#0086b3;">2
</span><span style="font-weight:bold;color:#a71d5d;">continue
esac
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mCloning \e[36m$</span><span style="color:#323232;">SCM</span><span style="color:#183691;">\e[32m repository from Bitbucket\e[0m"
</span><span style="color:#323232;">rm -rf </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-repo.git"
</span><span style="color:#323232;">git clone --mirror </span><span style="color:#183691;">"$</span><span style="color:#323232;">CLONE_ARG</span><span style="color:#183691;">" "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-repo.git"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mCreating new repository on GitHub\e[0m"
</span><span style="color:#323232;">$GH_CURL https://api.github.com/user/repos -d </span><span style="color:#183691;">"$(
</span><span style="color:#323232;">jq -n -c </span><span style="color:#183691;">\
</span><span style="color:#323232;"> --slurpfile</span><span style="color:#183691;"> p "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/repo" \
'{
name: $p[0].slug,
private: $p[0].is_private,
description: $p[0].description,
has_wiki: $p[0].has_wiki,
homepage: $p[0].website
}'
)" </span><span style="font-weight:bold;color:#a71d5d;">> </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/to-repo.json"
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#62a35c;">[ </span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r</span><span style="color:#183691;"> .ssh_url $</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/to-repo.json)" </span><span style="font-weight:bold;color:#a71d5d;">==</span><span style="color:#323232;"> null </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">then
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[31mUnable to create repository \e[36m$</span><span style="color:#323232;">SLUG</span><span style="color:#183691;">\e[31m, does it already exist?\e[0m" </span><span style="font-weight:bold;color:#a71d5d;">>&</span><span style="color:#0086b3;">2
</span><span style="font-weight:bold;color:#a71d5d;">continue
fi
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mPushing repository to GitHub\e[0m"
</span><span style="color:#323232;">GH_WEB_URL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r</span><span style="color:#183691;"> .html_url $</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/to-repo.json)"
</span><span style="color:#323232;">GH_REPO_URL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"$(</span><span style="color:#323232;">jq -r</span><span style="color:#183691;"> .ssh_url $</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/to-repo.json)"
</span><span style="color:#323232;">git -C </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-repo.git"</span><span style="color:#323232;"> push --mirror </span><span style="color:#183691;">"$</span><span style="color:#323232;">GH_REPO_URL</span><span style="color:#183691;">"
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#62a35c;">[ </span><span style="color:#183691;">"x$</span><span style="color:#323232;">HAS_WIKI</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">== </span><span style="color:#183691;">"xtrue" </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">then
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mCloning wiki repository from Bitbucket\e[0m"
</span><span style="color:#323232;">rm -rf </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo.git"
</span><span style="color:#323232;">git clone --mirror </span><span style="color:#183691;">"$</span><span style="color:#323232;">CLONE_ARG</span><span style="color:#183691;">/wiki" "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo.git"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mRenaming files for new naming convention\e[0m"
</span><span style="color:#323232;">rm -rf </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo"
</span><span style="color:#323232;">git clone </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo.git" "$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo"
</span><span style="font-weight:bold;color:#a71d5d;">export </span><span style="color:#323232;">TMP
find </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo"</span><span style="color:#323232;"> -depth -name </span><span style="color:#183691;">"*.wiki"</span><span style="color:#323232;"> -exec sh -c </span><span style="color:#183691;">'git -C "$TMP/cloned-wiki-repo" mv "$1" "${1%.wiki}.creole"'</span><span style="color:#323232;"> _ </span><span style="color:#183691;">'{}' </span><span style="color:#0086b3;">\;
</span><span style="color:#323232;">git -C </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo"</span><span style="color:#323232;"> commit -m </span><span style="color:#183691;">'Update naming scheme from Bitbucket to GitHub'
</span><span style="color:#323232;">git -C </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo"</span><span style="color:#323232;"> push
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[35mAction requred:"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[32mVisit \e[36mhttps://github.com/$</span><span style="color:#323232;">GH_USERNAME</span><span style="color:#183691;">/$</span><span style="color:#323232;">SLUG</span><span style="color:#183691;">/wiki/_new"
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-en </span><span style="color:#183691;">" \e[32mCreate a wiki page (contents irrelevant), then press ENTER to continue\e[0m"
</span><span style="color:#62a35c;">read
echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mPushing wiki repository to GitHub\e[0m"
</span><span style="color:#323232;">GH_WIKI_URL</span><span style="font-weight:bold;color:#a71d5d;">=</span><span style="color:#183691;">"${</span><span style="color:#323232;">GH_REPO_URL</span><span style="font-weight:bold;color:#a71d5d;">%</span><span style="color:#183691;">.git}.wiki.git"
</span><span style="color:#323232;">git -C </span><span style="color:#183691;">"$</span><span style="color:#323232;">TMP</span><span style="color:#183691;">/cloned-wiki-repo.git"</span><span style="color:#323232;"> push --mirror </span><span style="color:#183691;">"$</span><span style="color:#323232;">GH_WIKI_URL</span><span style="color:#183691;">"
</span><span style="font-weight:bold;color:#a71d5d;">fi
if </span><span style="color:#62a35c;">[ </span><span style="color:#183691;">"$</span><span style="color:#323232;">DELETE</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">== </span><span style="color:#183691;">"yes" </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">then
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mDeleting Bitbucket repository\e[0m"
</span><span style="color:#323232;">$BB_CURL -X DELETE </span><span style="color:#183691;">"$</span><span style="color:#323232;">SELF</span><span style="color:#183691;">?redirect_to=$</span><span style="color:#323232;">GH_WEB_URL</span><span style="color:#183691;">" </span><span style="font-weight:bold;color:#a71d5d;">></span><span style="color:#323232;"> /dev/null
</span><span style="font-weight:bold;color:#a71d5d;">fi
</span><span style="color:#62a35c;">echo </span><span style="color:#323232;">-e </span><span style="color:#183691;">" \e[1m\e[32mSuccessfully migrated \e[36m$</span><span style="color:#323232;">SLUG</span><span style="color:#183691;">\e[32m ✔\e[0m"
</span><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#62a35c;">[ </span><span style="color:#323232;">$ONE_REPO </span><span style="font-weight:bold;color:#a71d5d;">==</span><span style="color:#323232;"> true </span><span style="color:#62a35c;">]
</span><span style="font-weight:bold;color:#a71d5d;">then
break
fi
done
</span></code></pre>BSP rendering in Doomhttps://magnushoff.com/blog/doom-revolverconf/Tue, 03 Sep 2019 00:00:00 +0000<p>I gave a talk on the Doom rendering engine at <a href="https://revolverhuset.no/revolverconf-2019-2">RevolverConf 2019.2</a>. It is a
big topic, so I had to narrow down the scope. I chose to talk about the
binary space partitioning (BSP) rendering algorithm. This was the core
innovation in Doom which enabled it to run smoothly on ancient hardware and it
is still an important algorithm in rendering engines. I want to do a write-up
on it one day, but for now I defer to the talk (in Norwegian):</p>
<style>.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style><div class='embed-container'><iframe src='https://www.youtube.com/embed/hss-MGHSOAU' frameborder='0' allowfullscreen></iframe></div>
<p>You can also try <a href="https://github.com/maghoff/wad-render/tree/revolverconf-2019.2">my implementation</a> as it was at RevolverConf here:</p>
<style>
#screen {
width: 640px;
height: 480px;
}
canvas {
vertical-align: top;
}
/* map */
#map-container {
width: 100%;
}
#map-container>svg {
width: 100%;
}
line {
stroke: black;
stroke-width: 2px;
}
.portal {
stroke: #888;
}
/* camera */
.camera--sightline {
stroke: #41f4a9;
stroke-width: 3;
stroke-dasharray: 5, 5;
}
.camera--fov-left, .camera--fov-right {
stroke: #41f4a9;
stroke-width: 3;
}
.camera--focus {
cursor: grab;
fill: #41f4a9;
}
.camera--target {
cursor: grab;
stroke: #41f4a9;
stroke-width: 3;
stroke-dasharray: 5, 5;
fill: transparent;
}
.camera--target:hover {
stroke-dasharray: none;
}
.camera--direction {
stroke: #41f4a9;
stroke-width: 10;
stroke-linecap: round;
}
</style>
<div style="width: 640px; margin: 0 auto"><canvas id="screen" width="320px" height="200px"></canvas></div>
<div id="map-container"></div>
<script src="/doom/script.1.js"></script>
Textureshttps://magnushoff.com/blog/textures/Tue, 16 Jul 2019 00:00:00 +0000<p>Textures in Doom are a notch more complex than <a href="/blog/sprites/">sprites</a>: they are compositions
of multiple sprites. There are also a couple of additional indirections, one
which seems to make more sense than the other.</p>
<p>The first indirection is the TEXTURE1 and TEXTURE2 lumps. While both <a href="/blog/flats/">flats</a> and
sprites are lumps in the wad file in their own right, textures are nested one
level deeper. You have to read the TEXTURE1 and TEXTURE2 lumps to even find the
textures. I haven't been able to make a good guess as to why this indirection
exists. I have implemented support for listing the textures in <a href="https://github.com/maghoff/wad-gfx/">wad-gfx</a>:</p>
<pre style="background-color:#ffffff;">
<code>wad-gfx doom1.wad TEXTURE1 texture list
</code></pre>
<p>Each texture is defined by a name, dimensions and a list of patches. A patch in
this case is a sprite and a position on the texture for where to draw the
sprite. Rendering a texture is then a relatively simple matter of composing
these patches on top of one another, with the notable complexity of retaining
the transparency information.<sup class="footnote-reference"><a href="#1">1</a></sup></p>
<p>But there's another surprising layer of indirection! The patches do not
reference sprites by name, but by number. Specifically by index into the table
in the separate PNAMES lump. This seems to be an extra indirection only causing
overhead, but it is possible to eagerly resolve all the names in the PNAMES
lump such that finding the sprites while rendering the texture turns into a
quick lookup instead of a more elaborate search. Perhaps that's the reason for
the PNAMES indirection.</p>
<p>A human readable way to represent such a texture definition is the <a href="https://doom.fandom.com/wiki/DeuTex">DeuTex</a>
format, which I have implemented in <a href="https://github.com/maghoff/wad-gfx/">wad-gfx</a>:</p>
<pre style="background-color:#ffffff;">
<code>wad-gfx doom1.wad TEXTURE1 texture extract -I LITE2
; TextureName Width Height
LITE2 64 128
; PatchName Xoffset Yoffset
* WALL02_2 0 56
* WALL02_2 0 0
* FLAMP 5 0
* FLAMP 35 0
</code></pre>
<p>So, LITE2 is a composition of the two sprites WALL02_2 and FLAMP:</p>
<table style="margin-left: auto; margin-right: auto; text-align: center">
<tr style="vertical-align: bottom">
<td><img srcset="/assets/textures/wall02_2.png, /assets/textures/wall02_2-4.png 2x" src="/assets/textures/wall02_2.png"></td>
<td><img srcset="/assets/textures/flamp.png, /assets/textures/flamp-4.png 2x" src="/assets/textures/flamp.png"></td>
</tr>
<tr>
<td>WALL02_2</td>
<td>FLAMP</td>
</tr>
</table>
<p>Assembled, it looks like this:</p>
<table style="margin-left: auto; margin-right: auto">
<tr style="vertical-align: top">
<td style="position: relative; width: 128px; height: 306px; background: #ccc; padding: 0">
<img id="1" style="position: absolute; top: 134px" srcset="/assets/textures/wall02_2.png, /assets/textures/wall02_2-4.png 2x" src="/assets/textures/wall02_2.png">
<img id="2" style="position: absolute" srcset="/assets/textures/wall02_2.png, /assets/textures/wall02_2-4.png 2x" src="/assets/textures/wall02_2.png">
<img id="3" style="position: absolute; left: 10px" srcset="/assets/textures/flamp.png, /assets/textures/flamp-4.png 2x" src="/assets/textures/flamp.png">
<img id="4" style="position: absolute; left: 70px" srcset="/assets/textures/flamp.png, /assets/textures/flamp-4.png 2x" src="/assets/textures/flamp.png">
</td>
<td>
<input autocomplete=off id="i1" data-for="1" type=checkbox checked><label for="i1"><code>WALL02_2 0 56</code></label><br/>
<input autocomplete=off id="i2" data-for="2" type=checkbox checked><label for="i2"><code>WALL02_2 0 0</code></label><br/>
<input autocomplete=off id="i3" data-for="3" type=checkbox checked><label for="i3"><code>FLAMP 5 0</code></label><br/>
<input autocomplete=off id="i4" data-for="4" type=checkbox checked><label for="i4"><code>FLAMP 35 0</code></label><br/>
</td>
</tr>
</table>
<script type="text/javascript">
(function () {
function toggle(ev) {
let checkbox = ev.target;
let img = document.getElementById(checkbox.getAttribute('data-for'));
let visible = checkbox.checked;
img.hidden = !visible;
}
for (let input of document.querySelectorAll('input[type="checkbox"]')) {
input.addEventListener('input', toggle);
}
}());
</script>
<p>With a bash "one-liner" we can quickly identify the most complex texture, LITE3,
with a whooping 64 patches. That one must surely be interesting!</p>
<pre style="background-color:#ffffff;">
<code>for TX in $(wad-gfx doom1.wad TEXTURE1 texture list) ; do
echo $(wad-gfx doom1.wad TEXTURE1 texture extract -I $TX | wc -l) $TX
done | sort -n
</code></pre><div style="margin-left: auto; margin-right: auto; text-align: center">
<img srcset="/assets/textures/lite3.png, /assets/textures/lite3-4.png 2x" src="/assets/textures/lite3.png"><br/>
LITE3
</div>
<p>Well, that's a let down. This is just many tiny lighting details repeated 16
times. The reason for this is that the Doom engine tiles textures vertically
only at 128 pixel intervals, so usable textures must be 128 pixels tall.</p>
<p>References: <a href="http://fabiensanglard.net/gebbdoom/">GEBBDOOM</a> and the two Doom Wikis <a href="https://doom.fandom.com/wiki/TEXTURE1_and_TEXTURE2">doom.fandom.com</a> and
<a href="https://doomwiki.org/">doomwiki.org</a>.</p>
<div class="footnote-definition" id="1"><sup class="footnote-definition-label">1</sup>
<p>Doom does not fully implement this, giving rise to the <a href="https://doomwiki.org/wiki/Medusa_effect">Medusa effect</a>.</p>
</div>
Spriteshttps://magnushoff.com/blog/sprites/Thu, 21 Mar 2019 00:00:00 +0000<p>Sprites in Doom are stored in a custom format, tailor made for the rendering
engine and in a sense even for the final output medium. It has taken me a
while to implement support for this format in <a href="https://github.com/maghoff/wad-gfx/">wad-gfx</a> (formerly <a href="/blog/flats/">doom-gfx</a>),
not because of any complexity in the format, but because I had a hard time
figuring out how to most faithfully represent the source data in a normal
contemporary image file format.</p>
<h2 id="anamorphic-pixels">Anamorphic pixels</h2>
<p>If you were into computers back when Doom was released, it is not unlikely
that you have good feelings associated with the resolution 320x200 on a 4:3
monitor. This is what anything good was using back then. You can consider a
4:3 monitor to be 4 length units wide and 3 length units tall, for some length
unit. Dividing the screen into 320x200 then makes each pixel 4/320 by 3/200
length units square. Calculating it all out, the aspect ratio for each pixel
is (4/320):(3/200) = 5:6. Horrors! The pixels aren't square!</p>
<p>And it is not something you can simply look past, the pixels are a full 20%
taller than they are wide! So if we extract a sprite and display it
pixel-for-pixel directly on a modern screen with normal square pixels, it
looks squashed compared to the original game:</p>
<table style="margin-left: auto; margin-right: auto">
<tr style="vertical-align: bottom">
<td style="text-align: right; padding-right: 20px"><img style="width: 82px; height: 114px" src="/assets/sprites/trooa1-4-a.png" alt="Imp rendered with anamorphic pixels"></td>
<td><img style="width: 82px; height: 136.5px" src="/assets/sprites/trooa1-4.png" alt="Imp rendered with square pixels"></td>
</tr>
<tr>
<td style="text-align: right; padding-right: 20px">Pixel-for-pixel extraction</td>
<td>Stretched 120% vertically</td>
</tr>
</table>
<p>So, on the one hand there's the exact pixel-for-pixel extraction, and on the
other there's the visually faithful extraction. I opted for supporting both,
with the visually faithful mode as default and the pixel-for-pixel exact mode
behind the command line option <code>--anamorphic</code>.</p>
<p>More as a side-note, the PNG format (which wad-gfx uses) supports different
pixel aspect ratios via <a href="http://www.libpng.org/pub/png/book/chapter11.html#png.ch11.div.8">the <code>pHYs</code> chunk</a>. wad-gfx uses this to correctly
declare the pixel aspect for the anamorphic extractions, but I have yet to
encounter a single image editor or viewer that makes use of this for display…</p>
<h2 id="transparency">Transparency</h2>
<p>With indexed coloring, the typical way to have transparency in sprites is
to dedicate one color index to be transparent. Then you get to have a regular
2D array of pixels, each of which may or may not be transparent. This is the
established way, and PNG supports this technique. But it is of course not what
Doom does.</p>
<p>First, I should mention that the sprites are stored column by column instead
of the more common row major ordering. This is because everything vertical in Doom
– that is, everything except the <a href="/blog/flats/">flats</a> – is rendered column by column.
The restrictions on Doom's 3D projections makes sure that all lines that are
vertical in world-space end up vertical in screen-space, which in turn means
that rendering a column is a relatively simple matter of scaling instead of a
more advanced projection. It also ties in with the anamorphic pixels: The
pixels end up the same way on screen as stored, so the pixel aspect ratio
remains unchanged. The upshot of these details is that it is simpler for the
rendering engine to work in columns, so it does.</p>
<p>Painting sprites with a transparent color index requires you to have a check
for each pixel to decide whether or not to paint that pixel. So, in order to
paint <em>fewer</em> pixels on screen, you have to do <em>more</em> work. That doesn't seem
right, and Doom manages to do away with this inefficiency: Each column is
encoded as a series of spans and each span consists of a <code>top</code> position, a
<code>length</code> and then <code>length</code> bytes of pixel data. So, in a way, this implements
compression via <a href="https://en.wikipedia.org/wiki/Run-length_encoding">run-length encoding</a>. Another way to look at it is like a
more imperative description of how to paint the sprite, rather than a "dead"
dataformat.</p>
<p>In the end, this scheme allows sprites to use all the 256 colors of the
palette, and they do. So how should this be represented in a PNG when there is
no universally unused color index? I wound up supporting three different
output formats:</p>
<ul>
<li>Full color: Export the sprite as a 32bpp PNG with all the correct colors
and all the correct transparency. The downside of this format is that it
discards color index information, so if you wanted to use additional tools
to apply palette effects, this is not possible. This is the default export
format.</li>
<li>Indexed color: Export the sprite as an 8bpp PNG with all the correct colors
and <em>no</em> transparency. Additionally, you must specify a color index to be
used as a background.</li>
<li>Transparency mask: Export the transparency information as a PNG.
Transparent pixels are black, opaque pixels are white. This can be used as
a layer mask in image editing software and together with the indexed color
export represents the original data faithfully.</li>
</ul>
<table style="margin-left: auto; margin-right: auto; text-align: center">
<tr style="vertical-align: bottom">
<td><img style="width: 82px; height: 136.5px" src="/assets/sprites/trooa1-4.png" alt="Imp with transparency"></td>
<td><img style="width: 82px; height: 136.5px" src="/assets/sprites/trooa1-4-i.png" alt="Imp with opaque background"></td>
<td><img style="width: 82px; height: 136.5px" src="/assets/sprites/trooa1-4-m.png" alt="Transparency mask for imp"></td>
</tr>
<tr>
<td>Full color</td>
<td>Indexed</td>
<td>Mask</td>
</tr>
</table>
<h2 id="moving-forward">Moving forward</h2>
<p>So how does this help me towards my goal of rendering a Doom map? Why am I
even bothering with the sprites? Well, wall textures are vertical, like
sprites, and they can be transparent, like sprites. "Transparent?", I hear you
ask. Yes, there are a few wall textures with transparency for things like
fences. So they are stored like sprites!</p>
<p>But that is not quite true either. Wall textures are composed of <em>patches</em>,
which are stored like sprites. But the textures themselves are stored in
another format which composes patches at different positions. So we will have
to get back to textures another time.</p>
<p>But as a teaser, let's have a look at some patches:</p>
<table style="margin-left: auto; margin-right: auto; text-align: center">
<tr style="vertical-align: bottom">
<td><img srcset="/assets/sprites/sw11_1.png, /assets/sprites/sw11_1-4.png 2x" src="/assets/sprites/sw11_1.png"></td>
<td><img srcset="/assets/sprites/exit1.png, /assets/sprites/exit1-4.png 2x" src="/assets/sprites/exit1.png"></td>
<td><img srcset="/assets/sprites/exit2.png, /assets/sprites/exit2-4.png 2x" src="/assets/sprites/exit2.png"></td>
<td><img srcset="/assets/sprites/wall00_2.png, /assets/sprites/wall00_2-4.png 2x" src="/assets/sprites/wall00_2.png"></td>
</tr>
<tr>
<td>SW11_1</td>
<td>EXIT1</td>
<td>EXIT2</td>
<td>WALL00_2</td>
</tr>
</table>
Flatshttps://magnushoff.com/blog/flats/Tue, 15 Jan 2019 00:00:00 +0000<p>Of the different image formats in Doom, flats are the simplest. Now, all
images in Doom are flat, but flats are what they decided to call the textures
that go on the floors and ceilings. I'll save the wall textures and sprites
for another time.</p>
<p>Flats have predefined dimensions of 64 by 64 pixels. With 8 bits per pixel and
no compression or other fancy encoding, this works out to 4096 bytes, and
indeed all flats in <code>doom.wad</code> are 4096 bytes. The pattern is clear when
looking at the entries between <code>F_START</code> and <code>F_END</code> with <a href="https://github.com/maghoff/wad/">wad-ls</a>:</p>
<pre style="background-color:#2b303b; color:#c0c5ce;"><table>
<tr><td>1206 </td><td>0 </td><td>F_START</td></tr>
<tr><td>1207 </td><td>0 </td><td>F1_START</td></tr>
<tr><td>1208 </td><td>4096 </td><td>FLOOR0_1</td></tr>
<tr><td>1209 </td><td>4096 </td><td>FLOOR0_3</td></tr>
<tr><td>1210 </td><td>4096 </td><td>FLOOR0_6</td></tr>
<tr><td>1211 </td><td>4096 </td><td>FLOOR1_1</td></tr>
<tr><td>1212 </td><td>4096 </td><td>FLOOR1_7</td></tr>
<tr><td>1213 </td><td>4096 </td><td>FLOOR3_3</td></tr>
<tr><td>1214 </td><td>4096 </td><td>FLOOR4_1</td></tr>
<tr><td>⋮</td><td>⋮</td><td>⋮</td></tr>
</table></pre>
<p>According to <a href="http://web.archive.org/web/20150421212726/http://aiforge.net/test/wadview/dmspec16.txt">the Unofficial Doom Specs</a>,</p>
<blockquote>
<p>Each flat is 4096 raw bytes, making a square 64 by 64 pixels. This is pasted
onto a floor or ceiling with the same orientation as the automap would imply,
i.e. the first byte is the color at the NW corner, the 64th byte (byte 63,
0x3f) is the NE corner, etc.</p>
</blockquote>
<p>So, it is perfectly simple to read in a flat.</p>
<p>Due to the way the flats are projected into the scene in the game, they even
get to have a square pixel aspect ratio. It seems unlikely that it would be
anything else, but be advised that the other images have a pixel aspect ratio
of 5:6. More on that when we get to those.</p>
<p>The only complication we have here is how to map the color indices in the
image to RGB color values. We are obviously working with indexed colors here,
so each byte in the flat is an index into a palette, but which palette?
Looking into <code>doom.wad</code>, there are two likely suspects: <code>PLAYPAL</code> and
<code>COLORMAP</code>. And both are indeed relevant.</p>
<p><code>PLAYPAL</code> is 10,752 bytes large, but we are looking for 256 entries of 3
channels each, reasonably expecting one byte per channel. One palette <em>should</em>
take 768 bytes. Well, 768 divides 10,752 perfectly in 14, and it turns out
that Doom uses these different palettes for various full-screen effects.
(Source: <a href="http://fabiensanglard.net/gebbdoom/">Game Engine Black Book Doom</a>, chapter 5.13) Palette 0 is
the standard. Palettes 1 through 8 are for the various intensities of red when
you get hurt. Palettes 9 through 12 are for the bright flash when you pick up
an item and palette 13 is for when you are wearing the radiation suit.</p>
<p><code>COLORMAP</code>, as fits with the name, is used for mapping color indices to other
indices in the same palette. This is used for mapping the graphics during
painting to darker color tones. The lump is 8,704 bytes which gives room for
34 maps of 256 colors each. The 32 first maps are used for increasingly dark
color tones. The value here is that this allows for a measure of computational
colors even in an idexed coloring scheme. I understand that this was mostly
unheard of at the time. Colormap 32 is used for the god mode effect with
inverted white colors and colormap 33 is entirely black.</p>
<p>I've written <a href="https://github.com/maghoff/doom-gfx/">doom-gfx</a> to extract graphics with support for both these
subtleties, and I plan to extend it with support for sprites and textures as
well. Let's see what it can do:</p>
<div style="max-width: 100%; overflow-x: auto">
<table style="margin-left: auto; margin-right: auto;">
<thead><tr><td></td><td>Normal</td><td>Radiation suit</td><td>God mode</td></tr></thead>
<tbody>
<tr>
<td>FLOOR5_1</td>
<td><img src="/floor5_1.png" alt="FLOOR5_1" style="width: 128px; height: 128px"></td>
<td><img src="/floor5_1-rad.png" alt="FLOOR5_1 radiation suit" style="width: 128px; height: 128px"></td>
<td><img src="/floor5_1-god.png" alt="FLOOR5_1 god mode" style="width: 128px; height: 128px"></td>
</tr>
<tr>
<td>FLOOR5_2</td>
<td><img src="/floor5_2.png" alt="FLOOR5_2" style="width: 128px; height: 128px"></td>
<td><img src="/floor5_2-rad.png" alt="FLOOR5_2 radiation suit" style="width: 128px; height: 128px"></td>
<td><img src="/floor5_2-god.png" alt="FLOOR5_2 god mode" style="width: 128px; height: 128px"></td>
</tr>
</tbody>
</table>
</div>
E1M1https://magnushoff.com/blog/e1m1/Wed, 02 Jan 2019 00:00:00 +0000<p>I was gifted the <a href="http://fabiensanglard.net/gebbdoom/">Game Engine Black Book DOOM</a> for Christmas, to my
great pleasure. It will let me finally satisfy the curiosity that has been
lingering in me since '93. And I suspect it is not entirely without some
expectation that I get this book now, after having recreated the rendering
engine from Wolfenstein 3D in a <a href="https://www.youtube.com/watch?v=LuUb9Hrl-LQ&index=2&list=PLkx_om6kUyNudJoNAeS3jG7VXgwu4Io6C">live-coding session</a> at <a href="https://revolverhuset.no/revolverconf-2018-1">RevolverConf
2018.1</a>.</p>
<p>My primary goal here is to implement a rendering engine of the same kind that
Doom sports. But I should note that this will be for my educational benefit,
so there is no point in taking shortcuts or arriving at the goal as quickly as
possible. And this is not going to be quick no matter what.</p>
<p>So, I'll attack it piecemeal. I'll try to identify steps I can take along
the way that will bring me closer to the rendering engine, but will not
require me to set aside extended periods of concentration. Those are in short
supply.</p>
<p>These last few days, I have created <a href="https://github.com/maghoff/wad/">a library and command-line tools to work
with WAD files</a> (<code>wad-ls</code> and <code>wad-read</code>), a delightfully simple, if a
bit stupid, data format. With that as a building block, it was relatively
simple to <a href="https://github.com/maghoff/doom-levels">read and make sense of a map</a>, and even render it as an
SVG.</p>
<p>This is what I love about graphics programming. After a relatively small
effort, my results are clearly visible in the unmistakable shape of E1M1:</p>
<p><a href="/e1m1.svg"><img src="/e1m1.svg" alt="E1M1" /></a></p>
WikiLinkshttps://magnushoff.com/blog/wikilinks/Wed, 10 Oct 2018 00:00:00 +0000<p>I've been working on <a href="https://github.com/maghoff/sausagewiki">Sausagewiki</a> for a while now, but I have only recently
discovered how best to enable prolific linking between articles. I have now
made it possible to link to an article by just writing its title in square
brackets inline in the text; <code>I like [pie], I like [cake]</code>. This is a bit of a
lucky combination of an extension to Markdown and the way URLs are generated
for articles in Sausagewiki. Let's look at the URLs first.</p>
<p>When designing the URL generation mechanism, I had some goals in mind, based
on what I think works great and less great in existing wiki engines.</p>
<p>I wanted to allow the titles of pages to be <em>anything</em>. Many wiki engines,
famously including MediaWiki, use the URL as the only input to decide what
the title of the page is. This gives rise to the banner at the top of some
pages on Wikipedia that tells you the title is inaccurate. It could be that the title
should have started with a lower case letter or that there is supposed to be a
symbol in there which isn't allowed in the URL scheme. Sausagewiki goes the
other way and uses the given title to generate the URL.</p>
<p>I also wanted the URLs to be easy to deal with, both technically and mentally,
and a wiki engine should have good support for creating new pages by inventing
URLs.</p>
<p>To make them easy to deal with, the article URLs in Sausagewiki only consist
of lower case latin letters and the dash symbol. The process to convert
any string into such a string is well established and often used in blog
software. It is called slugging and the result of slugging a string is called
a slug. To make it easy to invent URLs and to be forgiving to people trying to
remember URLs, Sausagewiki will also accept URLs that do not
conform to the slug format. If you enter a path that is not a valid slug,
Sausagewiki will respond with a redirect to the slug of that path. Say you
navigate to <code>/Howl's Moving Castle</code>, you'll be redirected to
<code>/howl-s-moving-castle</code>. Similarly <code>/Soyanøttesmør</code> will be a redirect to
<code>/soyanottesmor</code>. These URLs are nice and easy and never require any special
encoding or escaping.</p>
<p>However, it means that including a link to the "Soyanøttesmør" article would
require you to write <code>[Soyanøttesmør](soyanottesmor)</code> or possibly
<code>[Soyanøttesmør](Soyanøttesmør)</code>, if you were willing to incur the cost of a
redirect. Finding the correct slug requires you to either run the slugging
algorithm in your head or creating the target article before linking to it.
This adds a speed bump where there shouldn't be one! Let's fix that.</p>
<p>There is another way to write links in Markdown, so-called <em>reference</em> links.
You write <code>I like [soyanøttesmør][1]</code> in one part of the text and on a
different line you define the link: <code>[1]: soyanottesmor</code>. The reference can be
any string, it doesn't have to be a number; <code>I like [soyanøttesmør][soyanøttesmør]</code> then <code>[soyanøttesmør]: soyanottesmor</code> works
the same. Notice that the reference definition at this point is a mapping from
a title to its slug. (References are case insensitive)</p>
<p>The <a href="https://daringfireball.net/projects/markdown/syntax">original Markdown definition</a> includes a shorthand form for
having the link text be the same as the reference text. The above example can
in this way be shortened to <code>I like [soyanøttesmør][]</code> while retaining the
reference definition. Several variants of Markdown, notably including
<a href="https://spec.commonmark.org/">CommonMark</a>, have shortened this further down to <code>I like [soyanøttesmør]</code>.
Neat! This is starting to look like the low effort linking a wiki engine
should support.</p>
<p>The only thing remaining now is to free the user from creating the reference
definition. In this case I was a bit lucky to find that the library I was
already using to process Markdown, <a href="https://github.com/raphlinus/pulldown-cmark">pulldown-cmark</a>, had support for taking a
callback to handle missing reference definitions. It was then simply a
question of plugging in the slugging function to allow short and sweet inline
wiki links.</p>
Wwwwayland suckshttps://magnushoff.com/blog/wayland-sucks/Thu, 31 May 2018 00:00:00 +0000<p>Wayland sucks. I'm sure that it's not <em>Wayland</em> that sucks, but running Ubuntu
18.04 with gnome-wayland is inferior to gnome-x11 in multiple surprising ways,
and better in only a few. I've been using gnome-wayland now for about a month,
since <a href="../system-upgrade/">upgrading to Ubuntu 18.04</a>, and I'm about ready to
switch back.</p>
<p>The good:</p>
<ul>
<li>In Wayland, I am allowed to switch workspaces with a four-finger swipe on
the trackpad. I'm using a MacBook Pro, so my trackpad is actually good, and
I've been missing this in X11.</li>
<li>With Wayland, the machine falls asleep/suspends to RAM much quicker than in
X11. Don't ask me to explain why there is a difference. It is comforting
that the screen backlight turns off immediately, so I don't have to wait to
confirm that it's actually going to go to sleep this time. (The
exceptionally few times it has failed to go to sleep has been when I have
experimented with settings in <code>powertop</code>. Fair enough!)</li>
<li>No tearing. Wayland actually delivers on this! But the software is not
mature, so, while the animation when switching workspaces does not tear, it
is not so easy with video playback. The reason is of course that when
falling back to running applications through the X11 compatibility layer,
tear-free playback can no longer be guaranteed. So I would have to use a
player that supports Wayland, and these are not mature. I'm sorting this as
a positive here, because it is straightforward to get the <em>same</em> experience
as with X11, plus there is an opportunity to experiment with something new.</li>
</ul>
<p>The bad:</p>
<ul>
<li>
<p>Immature ecosystem: <a href="https://github.com/Guake/guake">Guake</a> does not respond to a global hotkey,
Wayland-supporting video players have buggy rendering, pop-up windows
appear in the wrong places and so on.</p>
</li>
<li>
<p>Worse performance. Wayland promises better performance by eliminating some
indirections for programs putting graphics on the screen. However, the
performance I actually experience seems worse. This is definitely the case
for games, such as Factorio, that use the X11 API for getting the graphics
on screen. (The actual rendering is of course through OpenGL) The one
experience I've had with a game that uses Wayland directly, even though it
is a work in progress hobby project, is <em>even worse!</em> I experienced
latencies of 3-4 seconds for response to my input. I'm not sure what to
make of this other than "The Wayland ecosystem is completely immature".</p>
</li>
<li>
<p>Slow resume from sleep. Yes, that's right. The flip side of suspending more
quickly is apparently that it resumes more slowly. When I first bought my
Mac, running OS X, it would regularly resume from sleep more quickly than I
could open the lid. Newer versions of OS X do this more slowly, and that
has been a huge disappointment. Resume times in Linux has varied a lot but
these days it is near instant in gnome-x11. With gnome-wayland, it requires
7-8 seconds.</p>
</li>
<li>
<p>Worse input handling. As noted above, there are some input-related things
that are made possible in Wayland. However, there is a fundamental problem
with the way input events, or possibly events in general, are handled. When
the system experiences high load, for example when I'm compiling Rust code,
event handling doesn't progress as normal. Consider the situation of a key
press: There is a key down event, some time passes, and then a key release
event. Depending on the time passed and the key repeat settings, any number
of key press events are generated. When my system experiences high load,
the key release event regularly gets pushed back so even when I just press
a key for regular quick typing, I can get many repeated keys printed. Or
when I switch tabs in Firefox with the keyboard, it ends up jumping many
tabs instead of one. Or when I close a tab, all of them disappear. It is
similar for mouse movement events. Suddenly the cursor jumps while moving.</p>
<p>Full disclosure: I have configured my keyboard for really low delay and
high repeat rate, but this has never caused a problem in any other system.</p>
<p>In a poetic turn of events, this is what happened when I tried typing in
"Wayland sucks" for the title. This time I think I will let the error
stand.</p>
</li>
</ul>
System(d) upgradehttps://magnushoff.com/blog/system-upgrade/Sun, 06 May 2018 00:00:00 +0000<p>I have recently updated a server of mine from Ubuntu 16.04 LTS to 18.04 LTS.
As usual, it was a bumpy ride. The upgrade stopped half-way through with my
system in a stupid state, and I had to figure out how to fix things so that
apt would be able to continue the process. I don't understand why this keeps
happening when the resolutions are so daft. This time it amounted to removing
a good deal of packages with <code>dpkg</code>. I don't understand how it is valuable
that I do this myself – it's not as if I know what I am doing.</p>
<p>However, in the end I was able to upgrade my system – yay! – but there were
still some things to deal with, most notably my node.js services and
utilities. Node.js is rapidly climbing to the top of my list of
least-favourite technologies because things that used to work simply stop
working. It just falls apart.</p>
<p>I discovered <code>npm rebuild</code> which rebuilds native libraries inside
<code>node_modules</code> without fetching those modules from the Internet again. This is
extremely welcome to ward off any stupid inconsistency problems with <code>npm</code>
package resolving. And it did solve my immediate node.js problems. But there
was a problem lingering still, which I <em>thought</em> was the fault of node.js or
npm, but turned out in the end to be systemd.</p>
<p>I have been using systemd for running <code>storage-server</code>, a personal project, on
my box with socket activation. The <code>.socket</code>-file looks like this (it sets up
listening on a Unix domain socket, which nginx connects to):</p>
<pre style="background-color:#ffffff;">
<code>[Socket]
ListenStream=/run/storage-server.socket
[Install]
WantedBy=sockets.target
</code></pre>
<p>and the <code>.service</code>-file used to look like this:</p>
<pre style="background-color:#ffffff;">
<code>[Unit]
Description=Storage Server
[Service]
ExecStart=/usr/bin/node /opt/storage-server/index.js
</code></pre>
<p>This used to set up socket activation perfectly fine, but <a href="https://www.npmjs.com/package/systemd">the
systemd-library</a> had now started throwing error messages that "No or
too many" sockets were available. An unnecessarily and annoyingly vague error
message. I discovered that I was able to debug socket activation with the
<code>systemd-socket-activate</code> command line utility:</p>
<pre style="background-color:#ffffff;">
<code>systemd-socket-activate -l 127.0.0.1:8081 /usr/bin/node ./index.js
</code></pre>
<p>This worked like a charm, so the library, and indeed node.js, was off the
hook!</p>
<p>In the end it turned out that systemd now requires a <code>Requires</code> declaration in
the <code>.service</code>-file to get socket activation up and running, making my
<code>.service</code>-file look more like this:</p>
<pre style="background-color:#ffffff;">
<code>[Unit]
Description=Storage Server
Requires=storage-server.socket
[Service]
ExecStart=/usr/bin/node /opt/storage-server/index.js
</code></pre>
<p>This addition made the service run like before again.</p>
Blog!https://magnushoff.com/blog/blog/Sun, 08 Apr 2018 00:00:00 +0000<p>I'm experimentally adding a blog to this site. In this post, I'll introduce
the blog and discuss some specifics about how the blog and site in general is
technically put together.</p>
<p>I used to write a bit on <a href="https://plus.google.com/+MagnusHoff">Google+</a>, but it is neither a good blogging
platform nor a good social platform, so I have given up on that. Writing blog
posts on this site is technically superior, and I suspect I will get a bigger
audience posting them on Twitter than I got on Google+.</p>
<h2 id="what">What</h2>
<p>A lot of people will recognize this entire site as a blog already, but I'm
making the distinction between articles, which I will maintain and hopefully
improve or extend over time, and blog posts, which I will publish and then
mostly don't revisit. The appropriate topics are also different. Articles
should be more timeless than blogposts. And I'll write blog posts using the
first person.</p>
<p>This separation is similar to what Amit Patel does with <a href="https://www.redblobgames.com/">Red Blob
Games</a> for articles and a blog at <a href="http://simblob.blogspot.no/">Blobs in Games</a>. Patel is
my hero and inspiration for making good use of the medium when writing online
articles. My all-time favorite article there is <a href="https://www.redblobgames.com/grids/hexagons/">Hexagonal Grids</a>
which is both an excellent introduction to the topic and an excellent
reference guide for implementation. That is my guiding star for articles.
Patel has also been kind enough to share his <a href="http://simblob.blogspot.no/2018/01/thinking-about-writing.html">thoughts on writing</a>
with this separation. I'll try to make good use of his experience.</p>
<h2 id="how">How</h2>
<p>This site has always been a static site; I have some source files that I build
to generate a set of files that constitute the entire site. The files are then
served quite directly by a simple web server.</p>
<p>For the first years I used Amazon Web Services for the server side, with DNS,
CDN and S3 for the file backing. It was a good learning experience – AWS was
new and exciting back then – but in the end it was too complicated and not
good enough. (Two examples: S3 cannot support different <code>Accept-Encoding</code>
headers and CloudFront does nothing but add latency when your site does not
have frequent visits)</p>
<p>This February I changed the hosting scheme to using nginx on a simple cloud
machine. It is much simpler, reacts quicker to updates and works better. My
build scripts were tied to the AWS setup I used from before, so I took the
opportunity to also update these. I now use a third-party static site
generator, <a href="https://www.getgutenberg.io/">Gutenberg</a>. When I started out, static site generators
weren't a thing.</p>
<p>Gutenberg is in its infancy. So far I like the amount of control it gives me
and the structure it helps me to maintain. It is a bit awkward to include the
more elaborate articles which require separate build steps, but it is not
worse than the scripts I had before. It is written in my current favorite
programming language, Rust, so I might end up contributing to scratch my
itches. That's always a nice option to have, though I hope I don't have to
reach for it too often.</p>
<p>I'd like to offer separate RSS/Atom feeds for the articles and the blog posts,
but this is one of the technical limitations of Gutenberg: There is only one
feed, and it includes everything. Hopefully this will be fixed in time.</p>
<p>I'm hopeful that using a third-party static site generator will allow me to
improve the site more and fiddle less with details, and having a blog will let
me post more and wait less on perfection.</p>
Mustachio: Pull streaming Mustache templatinghttps://www.npmjs.com/package/mustachioFri, 14 Oct 2016 00:00:00 +0000<p>Mustachio: Pull streaming Mustache templating</p>
Solving Minesweeperhttps://magnushoff.com/minesweeperTue, 25 Aug 2015 00:00:00 +0000<p>
Minesweeper is a simple game with simple rules, yet some configurations
yield interesting challenges. In this article, we will develop a Minesweeper
solver of increasing refinement, and discuss how the game dynamics develop
as we employ the increasingly advanced help. In the end, we will develop a new
variant of the game which has more interesting gameplay.
</p>
<h2>Local reasoning: Zero adjacent mines</h2>
<p>
The <a href=https://en.wikipedia.org/wiki/Microsoft_Minesweeper>original game</a>
employs one automatic mechanism: When you reveal a square that has zero adjacent
mines, all the adjacent squares are revealed by the game engine. This
trivially has no risk, so it is safe to let the computer do it, and it is
trivially obvious to the player that this is the case, so it should not ruin any
fun to do it.
</p>
<p>
This reasoning is entirely local: information from only one square is used to
determine which action to take next.
</p>
<p>
It is hard to make the case that the game would be better without this automatic
help. Try the following game to get a feel for how it acts when there is no
automatic reveal:
</p>
<div class=center>
<div class=game-scrollbox><div class=game id=no-auto data-width=8 data-height=8 data-mines=10><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div></div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=no-auto>10</span><br>
<button class=restart-game data-for=no-auto></button><br>
<button class=rule data-toggle-rule=autoExploreZero data-for=no-auto>Automatic reveal</button>
</div></div>
<h2>Local with surroundings</h2>
<p>
It shouldn't take long for a new player to realize that when the number of
adjacent mines—that is, the number shown on the square—is the same as the number
of adjacent unrevealed squares, all of those squares must be mines, so they can
be marked as such. Similarly, when the number of adjacent mines equals the
number of adjacent flags, the remaining adjacent unrevealed squares must be safe.
</p>
<p>
These rules take into account one square and whether or not adjacent squares are
revealed or flagged.
</p>
<p>
Performing these rules manually can be entertaining. Combined with a timer, you
train to be able to execute these rules quickly and accurately. This makes
Minesweeper a <a href=https://en.wikipedia.org/wiki/Twitch_gameplay>twitch game</a>.
So what happens if we automate these rules?
</p>
<div class=center>
<div class=game-scrollbox>
<div class=game data-rules="autoExploreZero autoExplore autoFlag" id=auto-flag-and-reveal data-width=16 data-height=16 data-mines=40><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div>
</div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=auto-flag-and-reveal>40</span><br>
<button class=restart-game data-for=auto-flag-and-reveal></button><br>
<button class="rule checked" data-toggle-rule=autoFlag data-for=auto-flag-and-reveal>Automatic flagging</button><br>
<button class="rule checked" data-toggle-rule=autoExplore data-for=auto-flag-and-reveal>Automatic reveal</button><br>
</div></div>
<p>
One interesting side effect of making this automatic is that placing a flag in
the wrong place can now be an immediately fatal mistake.
</p>
<p>
For the rest of the game, we are left with situations that fall into three
categories:
</p>
<ol>
<li>Games that are completely resolved by the automatic rules</li>
<li>Elaborate situations that require reasoning involving more squares</li>
<li>Game states that have no logical way forward—the player is left with
no recourse other than to guess at random, maybe weighted by probability</li>
</ol>
<p>
While situation 1 above is kind of neat, it is hardly satisfying to play many
such games. Would those games have been better without the automatic resolution?
Maybe not; such games are very straightforward even when you solve them
manually, and it is not very satisfying to win at games where you have not felt
challenged. Of course, for a twitch game there is always some challenge in
acting more quickly.
</p>
<p>
I find situation 2 very satisfying. We get to focus more on resolving logical
requirements and less on accurately aiming and clicking the correct buttons.
This makes Minesweeper more of a
<a href=https://en.wikipedia.org/wiki/Puzzle_video_game#Action_puzzle>puzzle game</a>.
</p>
<p>
Situation 3, however, completely ruins the fun. Although I have heard about
people that like to play <a href=https://en.wikipedia.org/wiki/Game_of_chance>
games of chance</a>.
</p>
<p>
Can we eliminate situation 3?
</p>
<h2>Complete solution: Global reasoning</h2>
<p>
To algorithmically discover all logical necessities from the game state, we have
to resort to exhaustive search of all game states.
<a href=http://web.mat.bham.ac.uk/R.W.Kaye/minesw/ordmsw.htm>Minesweeper is
actually proven to be NP-complete</a>. The following is a small, but interesting
and illustrative example of a game state that has only one logical solution, but
you need to take into account the entire game state to find it:
</p>
<div class=center><div class=game-scrollbox><div id=logic-quiz class=game data-rules="autoExploreZero noReveal" data-initial-state=" xx / 2222 /x2002x/x2002x/ 2222 / xx "><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div></div><div style="display:inline-block; text-align:left">
<button class=restart-game data-for=logic-quiz></button><br>
</div></div>
<p>
Is it feasible to search the entire game state space? How many possible states,
<i>s</i>, are there?
</p>
<p>Given</p>
<div role=math class=center>
<table class=math-definitions>
<tr><td><i>w</i><td>= the width of the board</tr>
<tr><td><i>h</i><td>= the height of the board</tr>
<tr><td><i>k</i><td>= the number of mines</tr>
<tr class=splitter></tr>
<tr><td><i>n</i><td>= <i>w</i> · <i>h</i></tr>
</table>
</div>
<p>Then the number of possible states, <i>s</i>, is</p>
<div role=math class=center>
<table class=math-definitions>
<tr><td><i>s</i><td>= <svg role=img aria-label="n choose k" xmlns:xlink=http://www.w3.org/1999/xlink style="vertical-align: -2.333ex; margin-left: 0ex; margin-right: 0ex; margin-bottom: 1px; margin-top: 1px;" width=4.833ex height=5.667ex viewBox="0 -1473.4 2087 2446.9" xmlns=http://www.w3.org/2000/svg>
<defs>
<path stroke-width=10 id=E1-MJMAIN-28 d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width=10 id=E1-MJSZ3-28 d="M701 -940Q701 -943 695 -949H664Q662 -947 636 -922T591 -879T537 -818T475 -737T412 -636T350 -511T295 -362T250 -186T221 17T209 251Q209 962 573 1361Q596 1386 616 1405T649 1437T664 1450H695Q701 1444 701 1441Q701 1436 681 1415T629 1356T557 1261T476 1118T400 927T340 675T308 359Q306 321 306 250Q306 -139 400 -430T690 -924Q701 -936 701 -940Z"></path>
<path stroke-width=10 id=E1-MJMATHI-6E d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"></path>
<path stroke-width=10 id=E1-MJMATHI-6B d="M121 647Q121 657 125 670T137 683Q138 683 209 688T282 694Q294 694 294 686Q294 679 244 477Q194 279 194 272Q213 282 223 291Q247 309 292 354T362 415Q402 442 438 442Q468 442 485 423T503 369Q503 344 496 327T477 302T456 291T438 288Q418 288 406 299T394 328Q394 353 410 369T442 390L458 393Q446 405 434 405H430Q398 402 367 380T294 316T228 255Q230 254 243 252T267 246T293 238T320 224T342 206T359 180T365 147Q365 130 360 106T354 66Q354 26 381 26Q429 26 459 145Q461 153 479 153H483Q499 153 499 144Q499 139 496 130Q455 -11 378 -11Q333 -11 305 15T277 90Q277 108 280 121T283 145Q283 167 269 183T234 206T200 217T182 220H180Q168 178 159 139T145 81T136 44T129 20T122 7T111 -2Q98 -11 83 -11Q66 -11 57 -1T48 16Q48 26 85 176T158 471L195 616Q196 629 188 632T149 637H144Q134 637 131 637T124 640T121 647Z"></path>
<path stroke-width=10 id=E1-MJMAIN-29 d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width=10 id=E1-MJSZ3-29 d="M34 1438Q34 1446 37 1448T50 1450H56H71Q73 1448 99 1423T144 1380T198 1319T260 1238T323 1137T385 1013T440 864T485 688T514 485T526 251Q526 134 519 53Q472 -519 162 -860Q139 -885 119 -904T86 -936T71 -949H56Q43 -949 39 -947T34 -937Q88 -883 140 -813Q428 -430 428 251Q428 453 402 628T338 922T245 1146T145 1309T46 1425Q44 1427 42 1429T39 1433T36 1436L34 1438Z"></path>
</defs>
<g stroke=currentColor fill=currentColor stroke-width=0 transform="matrix(1 0 0 -1 0 0)">
<use xlink:href=#E1-MJSZ3-28 x=0 y=-1></use>
<g transform=translate(741,0)>
<use xlink:href=#E1-MJMATHI-6E x=0 y=676></use>
<use xlink:href=#E1-MJMATHI-6B x=39 y=-686></use>
</g>
<use xlink:href=#E1-MJSZ3-29 x=1346 y=-1></use>
</g>
</svg>
</tr>
</table>
</div>
<p>
For the standard beginner, intermediate and expert configurations, this works
out to:
</p>
<div role=math class=center>
<table class=math-definitions>
<tr><td><i>s</i><sub>beginner</sub> =<td><svg role=img aria-label="8 times 8, choose 10" xmlns:xlink=http://www.w3.org/1999/xlink style="vertical-align: -2.333ex; margin-left: 0ex; margin-right: 0ex; margin-bottom: 1px; margin-top: 1px;" width=7.5ex height=5.667ex viewBox="0 -1473.4 3219.4 2446.9" xmlns=http://www.w3.org/2000/svg>
<defs>
<path stroke-width=10 id=E1-MJMAIN-28 d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width=10 id=E1-MJSZ3-28 d="M701 -940Q701 -943 695 -949H664Q662 -947 636 -922T591 -879T537 -818T475 -737T412 -636T350 -511T295 -362T250 -186T221 17T209 251Q209 962 573 1361Q596 1386 616 1405T649 1437T664 1450H695Q701 1444 701 1441Q701 1436 681 1415T629 1356T557 1261T476 1118T400 927T340 675T308 359Q306 321 306 250Q306 -139 400 -430T690 -924Q701 -936 701 -940Z"></path>
<path stroke-width=10 id=E1-MJMAIN-38 d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"></path>
<path stroke-width=10 id=E1-MJMAIN-22C5 d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path>
<path stroke-width=10 id=E1-MJMAIN-31 d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path>
<path stroke-width=10 id=E1-MJMAIN-30 d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path>
<path stroke-width=10 id=E1-MJMAIN-29 d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width=10 id=E1-MJSZ3-29 d="M34 1438Q34 1446 37 1448T50 1450H56H71Q73 1448 99 1423T144 1380T198 1319T260 1238T323 1137T385 1013T440 864T485 688T514 485T526 251Q526 134 519 53Q472 -519 162 -860Q139 -885 119 -904T86 -936T71 -949H56Q43 -949 39 -947T34 -937Q88 -883 140 -813Q428 -430 428 251Q428 453 402 628T338 922T245 1146T145 1309T46 1425Q44 1427 42 1429T39 1433T36 1436L34 1438Z"></path>
</defs>
<g stroke=currentColor fill=currentColor stroke-width=0 transform="matrix(1 0 0 -1 0 0)">
<use xlink:href=#E1-MJSZ3-28 x=0 y=-1></use>
<g transform=translate(741,0)>
<g transform=translate(0,676)>
<use xlink:href=#E1-MJMAIN-38 x=0 y=0></use>
<use xlink:href=#E1-MJMAIN-22C5 x=727 y=0></use>
<use xlink:href=#E1-MJMAIN-38 x=1232 y=0></use>
</g>
<g transform=translate(363,-686)>
<use xlink:href=#E1-MJMAIN-31></use>
<use xlink:href=#E1-MJMAIN-30 x=505 y=0></use>
</g>
</g>
<use xlink:href=#E1-MJSZ3-29 x=2478 y=-1></use>
</g>
</svg></td>
<td>= 151 473 214 816</tr>
<tr><td><i>s</i><sub>intermediate</sub> =<td><svg role=img aria-label="16 times 16, choose 40" xmlns:xlink=http://www.w3.org/1999/xlink style="vertical-align: -2.333ex; margin-left: 0ex; margin-right: 0ex; margin-bottom: 1px; margin-top: 1px;" width=9.833ex height=5.667ex viewBox="0 -1473.4 4229.4 2446.9" xmlns=http://www.w3.org/2000/svg>
<defs>
<path stroke-width=10 id=E1-MJMAIN-28 d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width=10 id=E1-MJSZ3-28 d="M701 -940Q701 -943 695 -949H664Q662 -947 636 -922T591 -879T537 -818T475 -737T412 -636T350 -511T295 -362T250 -186T221 17T209 251Q209 962 573 1361Q596 1386 616 1405T649 1437T664 1450H695Q701 1444 701 1441Q701 1436 681 1415T629 1356T557 1261T476 1118T400 927T340 675T308 359Q306 321 306 250Q306 -139 400 -430T690 -924Q701 -936 701 -940Z"></path>
<path stroke-width=10 id=E1-MJMAIN-31 d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path>
<path stroke-width=10 id=E1-MJMAIN-36 d="M42 313Q42 476 123 571T303 666Q372 666 402 630T432 550Q432 525 418 510T379 495Q356 495 341 509T326 548Q326 592 373 601Q351 623 311 626Q240 626 194 566Q147 500 147 364L148 360Q153 366 156 373Q197 433 263 433H267Q313 433 348 414Q372 400 396 374T435 317Q456 268 456 210V192Q456 169 451 149Q440 90 387 34T253 -22Q225 -22 199 -14T143 16T92 75T56 172T42 313ZM257 397Q227 397 205 380T171 335T154 278T148 216Q148 133 160 97T198 39Q222 21 251 21Q302 21 329 59Q342 77 347 104T352 209Q352 289 347 316T329 361Q302 397 257 397Z"></path>
<path stroke-width=10 id=E1-MJMAIN-22C5 d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path>
<path stroke-width=10 id=E1-MJMAIN-34 d="M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z"></path>
<path stroke-width=10 id=E1-MJMAIN-30 d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path>
<path stroke-width=10 id=E1-MJMAIN-29 d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width=10 id=E1-MJSZ3-29 d="M34 1438Q34 1446 37 1448T50 1450H56H71Q73 1448 99 1423T144 1380T198 1319T260 1238T323 1137T385 1013T440 864T485 688T514 485T526 251Q526 134 519 53Q472 -519 162 -860Q139 -885 119 -904T86 -936T71 -949H56Q43 -949 39 -947T34 -937Q88 -883 140 -813Q428 -430 428 251Q428 453 402 628T338 922T245 1146T145 1309T46 1425Q44 1427 42 1429T39 1433T36 1436L34 1438Z"></path>
</defs>
<g stroke=currentColor fill=currentColor stroke-width=0 transform="matrix(1 0 0 -1 0 0)">
<use xlink:href=#E1-MJSZ3-28 x=0 y=-1></use>
<g transform=translate(741,0)>
<g transform=translate(0,676)>
<use xlink:href=#E1-MJMAIN-31></use>
<use xlink:href=#E1-MJMAIN-36 x=505 y=0></use>
<use xlink:href=#E1-MJMAIN-22C5 x=1232 y=0></use>
<g transform=translate(1737,0)>
<use xlink:href=#E1-MJMAIN-31></use>
<use xlink:href=#E1-MJMAIN-36 x=505 y=0></use>
</g>
</g>
<g transform=translate(868,-686)>
<use xlink:href=#E1-MJMAIN-34></use>
<use xlink:href=#E1-MJMAIN-30 x=505 y=0></use>
</g>
</g>
<use xlink:href=#E1-MJSZ3-29 x=3488 y=-1></use>
</g>
</svg></td>
<td>= 1.050 · 10<sup>47</sup>
</tr>
<tr><td><i>s</i><sub>expert</sub> =<td><svg role=img aria-label="30 times 16, choose 99" xmlns:xlink=http://www.w3.org/1999/xlink style="vertical-align: -2.333ex; margin-left: 0ex; margin-right: 0ex; margin-bottom: 1px; margin-top: 1px;" width=9.833ex height=5.667ex viewBox="0 -1473.4 4229.4 2446.9" xmlns=http://www.w3.org/2000/svg>
<defs>
<path stroke-width=10 id=E1-MJMAIN-28 d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"></path>
<path stroke-width=10 id=E1-MJSZ3-28 d="M701 -940Q701 -943 695 -949H664Q662 -947 636 -922T591 -879T537 -818T475 -737T412 -636T350 -511T295 -362T250 -186T221 17T209 251Q209 962 573 1361Q596 1386 616 1405T649 1437T664 1450H695Q701 1444 701 1441Q701 1436 681 1415T629 1356T557 1261T476 1118T400 927T340 675T308 359Q306 321 306 250Q306 -139 400 -430T690 -924Q701 -936 701 -940Z"></path>
<path stroke-width=10 id=E1-MJMAIN-33 d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"></path>
<path stroke-width=10 id=E1-MJMAIN-30 d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"></path>
<path stroke-width=10 id=E1-MJMAIN-22C5 d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250Z"></path>
<path stroke-width=10 id=E1-MJMAIN-31 d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"></path>
<path stroke-width=10 id=E1-MJMAIN-36 d="M42 313Q42 476 123 571T303 666Q372 666 402 630T432 550Q432 525 418 510T379 495Q356 495 341 509T326 548Q326 592 373 601Q351 623 311 626Q240 626 194 566Q147 500 147 364L148 360Q153 366 156 373Q197 433 263 433H267Q313 433 348 414Q372 400 396 374T435 317Q456 268 456 210V192Q456 169 451 149Q440 90 387 34T253 -22Q225 -22 199 -14T143 16T92 75T56 172T42 313ZM257 397Q227 397 205 380T171 335T154 278T148 216Q148 133 160 97T198 39Q222 21 251 21Q302 21 329 59Q342 77 347 104T352 209Q352 289 347 316T329 361Q302 397 257 397Z"></path>
<path stroke-width=10 id=E1-MJMAIN-39 d="M352 287Q304 211 232 211Q154 211 104 270T44 396Q42 412 42 436V444Q42 537 111 606Q171 666 243 666Q245 666 249 666T257 665H261Q273 665 286 663T323 651T370 619T413 560Q456 472 456 334Q456 194 396 97Q361 41 312 10T208 -22Q147 -22 108 7T68 93T121 149Q143 149 158 135T173 96Q173 78 164 65T148 49T135 44L131 43Q131 41 138 37T164 27T206 22H212Q272 22 313 86Q352 142 352 280V287ZM244 248Q292 248 321 297T351 430Q351 508 343 542Q341 552 337 562T323 588T293 615T246 625Q208 625 181 598Q160 576 154 546T147 441Q147 358 152 329T172 282Q197 248 244 248Z"></path>
<path stroke-width=10 id=E1-MJMAIN-29 d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"></path>
<path stroke-width=10 id=E1-MJSZ3-29 d="M34 1438Q34 1446 37 1448T50 1450H56H71Q73 1448 99 1423T144 1380T198 1319T260 1238T323 1137T385 1013T440 864T485 688T514 485T526 251Q526 134 519 53Q472 -519 162 -860Q139 -885 119 -904T86 -936T71 -949H56Q43 -949 39 -947T34 -937Q88 -883 140 -813Q428 -430 428 251Q428 453 402 628T338 922T245 1146T145 1309T46 1425Q44 1427 42 1429T39 1433T36 1436L34 1438Z"></path>
</defs>
<g stroke=currentColor fill=currentColor stroke-width=0 transform="matrix(1 0 0 -1 0 0)">
<use xlink:href=#E1-MJSZ3-28 x=0 y=-1></use>
<g transform=translate(741,0)>
<g transform=translate(0,676)>
<use xlink:href=#E1-MJMAIN-33></use>
<use xlink:href=#E1-MJMAIN-30 x=505 y=0></use>
<use xlink:href=#E1-MJMAIN-22C5 x=1232 y=0></use>
<g transform=translate(1737,0)>
<use xlink:href=#E1-MJMAIN-31></use>
<use xlink:href=#E1-MJMAIN-36 x=505 y=0></use>
</g>
</g>
<g transform=translate(868,-686)>
<use xlink:href=#E1-MJMAIN-39></use>
<use xlink:href=#E1-MJMAIN-39 x=505 y=0></use>
</g>
</g>
<use xlink:href=#E1-MJSZ3-29 x=3488 y=-1></use>
</g>
</svg></td>
<td>= 5.602 · 10<sup>104</sup>
</tr>
</table>
</div>
<p>
That's a solid "no", the naive approach is out the window. Let's examine what
the naive algorithm would have been and see if we can optimize it into something
workable.
</p>
<h3>The naive algorithm</h3>
<p>
The goal for the algorithm is to find all logical necessities from a given board
state. It is tricky to reason about this in a smart way; the computer is better
at doing lots of stupid things quickly.
</p>
<p>
What we can do stupidly, is to generate all possible permutations of mine
positions for all the remaining mines. If such a permutation fits with all the
revealed numbers it could be the correct solution for the game. When we explore
all the possible permutations, we find all possible solutions, but we still do
not know which is the correct one.
</p>
<p>
If there is something in common, either open squares or squares marked as mines,
between all the possible solutions, we know that this must be a part of the
correct solution for the current board. It is indeed impossible to create a
sound solution that does not have this in common, or else we would have
discovered it.
</p>
<p>
This way, we can find all the logical necessities from the current board state.
</p>
<h3>Constrained and unconstrained squares</h3>
<p>
The immediate problem with the above algorithm is the number of states it has to
explore, as calculated above. But not all squares are the same. The unrevealed
squares that are adjacent to a number are obviously constrained by this number.
We will call these squares the constrained squares. We will call the remaining
squares the unconstrained squares.
</p>
<p>
If we implement the above algorithm but only search the state space of the
constrained squares, and we make sure to backtrack as soon as we violate a
constraint, we can resolve all logical requirements in a reasonable amount of
time for many games:
</p>
<div class=center>
<div class=game-scrollbox><div id=logic-quiz-auto class=game data-rules="autoExploreZero noReveal" data-initial-state=" xx / 2222 /x2002x/x2002x/ 2222 / xx "><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq m-2"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div></div>
<div style="display:inline-block; text-align:left">
<button class=restart-game data-for=logic-quiz-auto></button><br>
<button class=execute-global-solver data-for=logic-quiz-auto>Run AI</button><br>
</div></div>
<p>
For the unconstrained squares we have no way of knowing where the mines go, and
we know this logically up front. This means we can remove them from the
calculation and only consider the placement of mines adjacent to revealed
numbers.
</p>
<p>
However, we do know that some amount of mines can go into the set
of unconstrained squares; if there are 6 mines and 4 constrained squares, at
most 4 mines go in the constrained squares, hence at least 2 mines go in the
unconstrained squares. By similar logic we can sometimes determine that all the
unconstrained squares must be empty or that all of them have mines.
</p>
<p>
For the following case, we know the position of all the mines, so the AI should
be able to realize that the remaining squares are unoccupied:
</p>
<div class=center><div class=game-scrollbox><div class=game data-rules="autoExploreZero autoExplore autoFlag" id=global-solver-case-1 data-initial-state="0000/0122/02ff/02f "><div class=row><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span></div><div class=row><span class="sq m-0"></span><span class="sq m-1"></span><span class="sq m-2"></span><span class="sq m-2"></span></div><div class=row><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow-mine"></span><span class="sq fow-mine"></span></div><div class=row><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow-mine"></span><span class="sq fow"></span></div></div></div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=global-solver-case-1>0</span><br>
<button class=restart-game data-for=global-solver-case-1></button><br>
<button class=execute-global-solver data-for=global-solver-case-1>Run AI</button><br>
</div></div>
<p>
For the next case, we don't know the position of all the mines, but we can tell
that we need to place the remaining mine in one of the two upper left squares.
This means that the remaining square in the lower right corner is free:
</p>
<div class=center><div class=game-scrollbox><div class=game data-rules="autoExploreZero autoExplore autoFlag" id=global-solver-case-2 data-initial-state="x f20000/13f20122/011102ff/000002f "><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow-mine"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span></div><div class=row><span class="sq m-1"></span><span class="sq m-3"></span><span class="sq fow-mine"></span><span class="sq m-2"></span><span class="sq m-0"></span><span class="sq m-1"></span><span class="sq m-2"></span><span class="sq m-2"></span></div><div class=row><span class="sq m-0"></span><span class="sq m-1"></span><span class="sq m-1"></span><span class="sq m-1"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow-mine"></span><span class="sq fow-mine"></span></div><div class=row><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-0"></span><span class="sq m-2"></span><span class="sq fow-mine"></span><span class="sq fow"></span></div></div></div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=global-solver-case-2>1</span><br>
<button class=restart-game data-for=global-solver-case-2></button><br>
<button class=execute-global-solver data-for=global-solver-case-2>Run AI</button><br>
</div></div>
<h3>The chance version</h3>
<p>
If we automatically execute the global solver, we get the chance optimized
version of Minesweeper:
</p>
<div class=center>
<div class=game-scrollbox>
<div class=game data-rules="autoExploreZero autoExplore autoFlag autoGlobal" id=global-solver-expert data-width=30 data-height=16 data-mines=99><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div>
</div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=global-solver-expert>99</span><br>
<button class=restart-game data-for=global-solver-expert></button><br>
<input type=radio class=rule-set name=global-solver-expert-rules id=global-solver-expert-rules-0 data-rule-set=autoExploreZero data-for=global-solver-expert>
<label for=global-solver-expert-rules-0>No extra automation</label><br>
<input type=radio class=rule-set name=global-solver-expert-rules id=global-solver-expert-rules-1 data-rule-set="autoExploreZero autoExplore autoFlag" data-for=global-solver-expert>
<label for=global-solver-expert-rules-1>Resolve local constraints</label><br>
<input type=radio class=rule-set name=global-solver-expert-rules id=global-solver-expert-rules-2 checked data-rule-set="autoExploreZero autoExplore autoFlag autoGlobal" data-for=global-solver-expert>
<label for=global-solver-expert-rules-2>Resolve all constraints</label><br>
<span class="activity-feedback stuck" data-for=global-solver-expert>AI is <span class=stuck>idle</span><span class=thinking>working</span></span><br>
</div></div>
<p>
We can kind of divide the games in this version into three categories:
</p>
<ol>
<li>Games where you make some arbitrary choices, and you win</li>
<li>Games where you make some arbitrary choices, and you lose</li>
<li>Games where the AI takes a long time to execute, and you can actually do
some reasoning</li>
</ol>
<p>
This is clearly a game of chance. What is the allure of such games? In a logical
sense, the above game is similar to the following one:
</p>
<div class=center>
<div class=game-scrollbox style="vertical-align: middle">
<div class=game data-rules=noMoveMine id=coin-flip data-width=2 data-height=1 data-mines=1><div class=row><span class="sq fow"></span><span class="sq fow"></span></div></div>
</div>
<div style="display:inline-block; text-align:left; vertical-align: middle">
<button class=restart-game data-for=coin-flip></button><br>
</div></div>
<p>
But which is the better chance game? It seems that other games of chance make a
point of having an elaborate connection between your actions and whether or not
you win. Drawings of lottery numbers use complicated machines that deliberately
take time to select a number and make a show of which number is chosen.
</p>
<p>
Maybe the large board that is automatically resolved is a somewhat good chance
game, given the show of watching all the squares be revealed.
</p>
<p>
Can we make another type of game?
</p>
<h3>The deterministic version</h3>
<p>
We now have an AI that can determine what all the logical steps from a given
game state is. Sometimes it will find no logical steps. These are the situations
where the player has to guess and may lose the game because of bad luck.
</p>
<p>
What if we add a new rule? When the game has no logical way forward, you can ask
for help. If the AI agrees that there is nothing you can do, you get help.
Otherwise, you immediately lose the game. This could be interesting. What should
the help be? Maybe that you get to reveal one square regardless of whether or
not it holds a mine:
</p>
<div class=center>
<div class=game-scrollbox>
<div class=game data-rules="autoExploreZero autoExplore autoFlag" id=get-help-rule data-width=30 data-height=16 data-mines=99><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div><div class=row><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span><span class="sq fow"></span></div></div>
</div>
<div style="display:inline-block; text-align:left">
Mines left: <span class=mines-left data-for=get-help-rule>99</span><br>
<button class=restart-game data-for=get-help-rule></button><br>
<input type=radio class=rule-set name=get-help-rule-rules id=get-help-rule-rules-0 data-rule-set=autoExploreZero data-for=get-help-rule>
<label for=get-help-rule-rules-0>No extra automation</label><br>
<input type=radio class=rule-set name=get-help-rule-rules id=get-help-rule-rules-1 checked data-rule-set="autoExploreZero autoExplore autoFlag" data-for=get-help-rule>
<label for=get-help-rule-rules-1>Resolve local constraints</label><br>
<button class=get-help data-for=get-help-rule>Request help</button>
<span class="activity-feedback stuck" data-for=get-help-rule><span class=stuck></span><span class=thinking>working</span><span class=granted>Granted!</span></span><br>
</div></div>
<p>
At this point, we have truly gotten rid of the game forcing you into situations
where you can lose by chance.
</p>
<p>
There is only one exception to this, namely that it is still possible to get
into degenerate situations for which the global solver will not finish
evaluating in a reasonable amount of time. This is unfortunately an unescapable
result of the problem being NP-complete, as noted above.
</p>
<p>
How does the "Request help" button affect the gameplay? It makes for a logically
focused game; this is the ultimate puzzle game variant of Minesweeper. One might
think that the game would become easier, but instead it gets harder. You no
longer have any excuse to make mistakes, and the button is sure to call you out
on anything you might have missed. Without the button it is easy to come to the
conclusion that you have exhausted all logical possibilities and the only course
of action is to guess randomly. With the button, you have to be correct in this
assessment.
</p>
<h2>Concluding remarks</h2>
<p>
By implementing a full solver for Minesweeper, we were able to develop a variant
of the game that gets rid of the bane of Minesweeper; when you risk losing the
game randomly after you have invested time and thought into solving almost all
of the board. This version is different from the original only in the situations
that would require random guessing, so I would suggest that this version is
strictly more fun than the original game.
</p>
<p>
We also developed a variant that will automatically resolve the simple local
rules. Whether or not you employ this help is more a matter of taste. It shifts
the focus of the game to be less mechanical and more puzzle focused, and is not
necessary for getting the improved gameplay that the "Request help" button
gives.
</p>
JPEG Orientationhttps://magnushoff.com/jpeg-orientation.htmlTue, 10 Sep 2019 00:00:00 +0000<p>When shooting a photograph in portrait orientation, cameras generally
recognize the situation and are able to store the resulting image such that the
top of the photo corresponds with the top of the scene. Yet somehow, portrait
photos sometimes show up in the wrong orientation; the top of the image on
screen suddenly corresponds to the top of the camera as it was when the photo
was taken. The culprit in this situation is lack of support for the EXIF
orientation tag.</p>
<h2>The EXIF orientation tag</h2>
<p>JPEG is a standard for compressing and encoding images. However, a
.jpg file generally contains information in addition to the image.
The file format of such a .jpg file is usually the Exchangeable image file
format, EXIF, which specifies how to include these details, including the
geographic coordinates where the photo was taken, timestamp, details about the
camera that was used and its settings and, the subject of our study,
specification of which orientation the the camera was held in as it took the
photo.</p>
<p>A camera will always store the image data such that the top of the image
corresponds to the top of the camera. It will use its orientation sensor to
find a value for the orientation tag to allow compliant software to show the
photo the right way up. This is a very simple strategy to implement in a camera
compared to rotating the full image data to match, at the expense of requiring
all JPEG decoders to be able to compensate for this. As you no doubt have
realized, this is a complication many decoders simply ignore.</p>
<p>The orientation tag can assume one of eight values, one for each possible
rotation as well as a mirrored version of each of those rotations. We will
develop an algorithm to correct the orientation of a .jpg file where the
presence of the mirrored variants will make sense even if they do not make sense
from a camera user's perspective.</p>
<p>The eight possible orientations are presented in the following table. The
correct rendering in all cases looks like a normal F. Software that does not
take the orientation tag into account will render a result that looks like the
second row. The images in this row do not have the EXIF orientation tag set. The
images in the last row have the orientation tag set, and compliant software
should render the last row to look like the first row.</p>
<link rel='stylesheet' type='text/css' href='/pygments.friendly.css'></link>
<style>
.figure {
margin-left: auto;
margin-right: auto;
overflow: auto;
}
table {
margin-left: auto;
margin-right: auto;
}
th {
font-weight: normal;
}
td {
width: 40px;
text-align: center;
}
td>img {
max-width: 40px;
max-height: 40px;
}
.ghost {
opacity: 0.2;
}
td:first-child {
white-space: nowrap;
text-align: right;
}
.transform>img {
transition: transform 0.8s;
transform-origin: center center;
}
.flip-diagonal:hover>img {
transform: rotate3d(1, 1, 0, 180deg);
}
.rotate-180:hover>img {
transform: rotatez(180deg);
}
.flip-horizontal:hover>img {
transform: rotatey(180deg);
}
.maths {
white-space: nowrap;
}
.maths td:last-child {
text-align: left;
}
table.matrix {
display: inline-table;
vertical-align: middle;
margin: 0;
padding: 0;
}
.bracket {
border-collapse: collapse;
line-height: 12px;
}
.bracket.matrix td, .bracket.matrix tr {
border: none;
padding: 0;
margin: 0;
}
.matrix td {
width: auto;
padding: 0 5px;
}
.simple td {
white-space: nowrap;
}
.simple td:first-child {
text-align: center
}
</style>
<div class="figure">
<table>
<thead>
<tr>
<td>EXIF Orientation Tag:</td>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
</tr>
</thead>
<tbody>
<tr>
<td>Correct rendering:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
</tr>
<tr>
<td>Typical incorrect rendering:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f3.jpg"></td>
<td><img src="/assets/f4.jpg"></td>
<td><img src="/assets/f5.jpg"></td>
<td><img src="/assets/f6.jpg"></td>
<td><img src="/assets/f7.jpg"></td>
<td><img src="/assets/f8.jpg"></td>
</tr>
<tr>
<td>Your browser's attempt:</td>
<td><img src="/assets/f1t.jpg"></td>
<td><img src="/assets/f2t.jpg"></td>
<td><img src="/assets/f3t.jpg"></td>
<td><img src="/assets/f4t.jpg"></td>
<td><img src="/assets/f5t.jpg"></td>
<td><img src="/assets/f6t.jpg"></td>
<td><img src="/assets/f7t.jpg"></td>
<td><img src="/assets/f8t.jpg"></td>
</tr>
</tbody>
</table>
</div>
<p>You can <a href="/assets/test-exiforientation.zip" download>download these images</a>
to test your favorite photo software. As a baseline, you can use ImageMagick,
which explicitly handles the orientation tag when you specify the
<code>-auto-orient</code> command line option:</p>
<pre class="pygments"><span class="c"># Display without orientation correction:</span>
display f6t.jpg
<span class="c"># Explicitly correct orientation:</span>
display -auto-orient f6t.jpg
</pre>
<h2>Correcting the orientation</h2>
<p>We could make a table of transformations to apply to images with given
orientation tags. After all, eight different transformations and eight different
code paths is not that much:</p>
<table class="figure simple">
<thead>
<tr><th>Tag</th><th>Operation</th></tr>
</thead>
<tr><td>1</td><td>do nothing</td></tr>
<tr><td>2</td><td>flip horizontally</td></tr>
<tr><td>3</td><td>rotate 180°</td></tr>
<tr><td>4</td><td>flip vertically</td></tr>
<tr><td>⋮ </td><td>⋮ </td></tr>
</table>
<p>But we will not do that. Instead we will exploit a structure in the values
allowing us to consider only three operations and combinations of those.</p>
<p>For eight different values, it is tempting to use values in the range [0..7]
rather than [1..8] which is used in EXIF. If we subtract one from the tag value,
maybe we can discern some binary structure?</p>
<div class="figure">
<table>
<thead>
<tr>
<td>EXIF Orientation Tag:</td>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
</tr>
</thead>
<tbody>
<tr>
<td>Subtract one:</td>
<th>0</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
</tr>
<tr>
<td>In binary:</td>
<th>000</th>
<th>001</th>
<th>010</th>
<th>011</th>
<th>100</th>
<th>101</th>
<th>110</th>
<th>111</th>
</tr>
<tr>
<td>Base image:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f3.jpg"></td>
<td><img src="/assets/f4.jpg"></td>
<td><img src="/assets/f5.jpg"></td>
<td><img src="/assets/f6.jpg"></td>
<td><img src="/assets/f7.jpg"></td>
<td><img src="/assets/f8.jpg"></td>
</tr>
</tbody>
</table>
</div>
<p>Let's consider the different cases. Which transformation should we apply to
flip bit zero? In other words; which transformation should we apply to convert
image 001 to image 000? Horizontal flip. For bit 1, image 010 to image 000, we
need to rotate by 180°. Finally, for bit 2, image 100 to image 000, we need
to flip along the diagonal.</p>
<p>It turns out that we can resolve all the remaining cases by combining these
fundamental operations according to the bit patterns of the orientation tags:</p>
<div class="figure">
<table>
<thead>
<tr>
<td>Binary tag:</td>
<th>000</th>
<th>001</th>
<th>010</th>
<th>011</th>
<th>100</th>
<th>101</th>
<th>110</th>
<th>111</th>
</tr>
<tr>
<td>Base image:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f3.jpg"></td>
<td><img src="/assets/f4.jpg"></td>
<td><img src="/assets/f5.jpg"></td>
<td><img src="/assets/f6.jpg"></td>
<td><img src="/assets/f7.jpg"></td>
<td><img src="/assets/f8.jpg"></td>
</tr>
<tr>
<td>Flip diagonally (1__):</td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td><img src="/assets/f2.jpg" class="ghost"></td>
<td><img src="/assets/f3.jpg" class="ghost"></td>
<td><img src="/assets/f4.jpg" class="ghost"></td>
<td class="transform flip-diagonal"><img src="/assets/f1.jpg"></td>
<td class="transform flip-diagonal"><img src="/assets/f2.jpg"></td>
<td class="transform flip-diagonal"><img src="/assets/f3.jpg"></td>
<td class="transform flip-diagonal"><img src="/assets/f4.jpg"></td>
</tr>
<tr>
<td>Rotate 180° (_1_):</td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td><img src="/assets/f2.jpg" class="ghost"></td>
<td class="transform rotate-180"><img src="/assets/f1.jpg"></td>
<td class="transform rotate-180"><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td><img src="/assets/f2.jpg" class="ghost"></td>
<td class="transform rotate-180"><img src="/assets/f1.jpg"></td>
<td class="transform rotate-180"><img src="/assets/f2.jpg"></td>
</tr>
<tr>
<td>Flip horizontally (__1):</td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td class="transform flip-horizontal"><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td class="transform flip-horizontal"><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td class="transform flip-horizontal"><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg" class="ghost"></td>
<td class="transform flip-horizontal"><img src="/assets/f1.jpg"></td>
</tr>
</tbody>
</table>
</div>
<p>An an implementation concern, it is worth noting that the transformations
could be expressed as transformation matrices which can be combined into one
composite transformation before applying it to the image data. There should be
no need to transform the image data in more than one pass. For completeness, the
different transformation matrices are:</p>
<div class="figure">
<table class="maths">
<tr><td>T<sub>flip-diagonal</sub></td><td>=</td><td>
<table class="bracket matrix"><tr><td>⎡</td></tr><tr><td>⎣</td></tr></table>
<table class="matrix">
<tr><td>0</td><td>1</td></tr>
<tr><td>1</td><td>0</td></tr></table>
<table class="bracket matrix"><tr><td>⎤</td></tr><tr><td>⎦</td></tr></table>
</td>
</tr>
<tr><td>T<sub>rotate-180</sub></td><td>=</td><td>
<table class="bracket matrix"><tr><td>⎡</td></tr><tr><td>⎣</td></tr></table>
<table class="matrix">
<tr><td>-1</td><td>0</td></tr>
<tr><td>0</td><td>-1</td></tr></table>
<table class="bracket matrix"><tr><td>⎤</td></tr><tr><td>⎦</td></tr></table>
</td>
</tr>
<tr><td>T<sub>flip-horizontal</sub></td><td>=</td><td>
<table class="bracket matrix"><tr><td>⎡</td></tr><tr><td>⎣</td></tr></table>
<table class="matrix">
<tr><td>-1</td><td>0</td></tr>
<tr><td>0</td><td>1</td></tr></table>
<table class="bracket matrix"><tr><td>⎤</td></tr><tr><td>⎦</td></tr></table>
</td>
</tr>
</table>
</div>
<p>The algorithm can thus be described by this pseudocode:</p>
<div class="pygments"><pre>
x = value of EXIF orientation tag
y = x-1
t = identity matrix
if y & 100b != 0 then t = t * T<sub>flip-diagonally</sub>
if y & 010b != 0 then t = t * T<sub>rotate-180</sub>
if y & 001b != 0 then t = t * T<sub>flip-horizontally</sub>
apply t to image
</pre></div>
<h2>Other failure modes</h2>
<p>Gwenview, the standard image viewer in KDE, has for a long time been a good
citizen, handling the orientation tag properly. Recently, however, the library
underlying Gwenview, Qt, has also acquired this feature. This results in the
good citizen suddenly <a href="https://bugs.kde.org/show_bug.cgi?id=347652">double
compensating for the stored orientation</a>. This is an
extra fun failure mode, because it looks a lot like the standard failure mode
where no effort is spent on correcting. It ends up working this way because all
the fundamental transformations are their own inverse, and only one of them is
not commutative. Double compensating is only distinguishable from not
compensating at all in the case of orientation tag values 6 and 8.</p>
<div class="figure">
<table class="figure">
<thead>
<tr>
<td>EXIF Orientation Tag:</td>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
<th>6</th>
<th>7</th>
<th>8</th>
</tr>
</thead>
<tbody>
<tr>
<td>Uncorrected images:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f3.jpg"></td>
<td><img src="/assets/f4.jpg"></td>
<td><img src="/assets/f5.jpg"></td>
<td><img src="/assets/f6.jpg"></td>
<td><img src="/assets/f7.jpg"></td>
<td><img src="/assets/f8.jpg"></td>
</tr>
<tr>
<td>Singly corrected images:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f1.jpg"></td>
</tr>
<tr>
<td>Doubly corrected images:</td>
<td><img src="/assets/f1.jpg"></td>
<td><img src="/assets/f2.jpg"></td>
<td><img src="/assets/f3.jpg"></td>
<td><img src="/assets/f4.jpg"></td>
<td><img src="/assets/f5.jpg"></td>
<td><img src="/assets/f8.jpg"></td>
<td><img src="/assets/f7.jpg"></td>
<td><img src="/assets/f6.jpg"></td>
</tr>
</tbody>
</table>
</div>
<h2>Acknowledgements</h2>
<p>Thanks to <a href="http://jpegclub.org/">the JPEG Club</a> for <a
href="http://jpegclub.org/exif_orientation.html">explaining the tag</a> and, I
guess, for maintaining jpegtran, which I used to generate the example images on
this page.</p>
<p>A great thanks to <a href="http://tilloy.net/dev/pyexiv2/">pyexiv2</a>, which
I used to <a href="/assets/set-orient.py">set the EXIF orientation tag</a> of .jpg files
without preexisting EXIF data. I'd prefer to use Python 3 and
<a href="https://wiki.gnome.org/Projects/gexiv2">GExiv2</a>, but it does not
seem to be packaged for Ubuntu 15.04, so there you go.</p>
Asmtuthttps://magnushoff.com/asmtut.htmlSun, 03 May 2020 00:00:00 +0000<p>I have written six lessons in a tutorial on x86-64 assembly programming. The
theme of the tutorial is the making of a snake game with the least amount of
abstractions over the OS.</p>
<p>The tutorial was originally posted on Google+, but has been reposted here
after its demise.</p>
Snygghttps://magnushoff.com/snygg/Tue, 17 Sep 2019 00:00:00 +0000<p>Edit the template file directly.</p>
Actually getting started with Portable Native Clienthttps://magnushoff.com/blog/pnacl/Mon, 09 Sep 2019 00:00:00 +0000<p><em>This post was originally a featured article on the <a href="/">front page</a>. I thought
the technology looked fun and promising, and I wanted to keep up with it. It
is clear, however, that the winner in this space is WASM, and PNaCl is just a
failed experiment.</em></p>
<p><em>While blog posts can go out of date and be kept for posterity, this is not
what I want the fetaured articles on my front page to be like, so I relegated
this text to the blog on 2019-09-09.</em></p>
<hr />
<p>We will develop a minimal <a href="https://developers.google.com/native-client/dev/" title="Native Client is a sandbox for running compiled C and C++ code in the browser efficiently and securely, independent of the user's operating system. Portable Native Client extends that technology with architecture independence, letting developers compile their code once to run in any website and on any architecture.">PNaCl</a>
module with a focus on exposing all the details
of the development process, leaving as little as possible to opaque scripts and
prewritten solutions. This should give you a solid foundation on which you can build
a full understanding of PNaCl.</p>
<p>There are several APIs you can use to create a pnacl module. In an effort
to actually get started, we will start out with ppapi_simple, which introduces
the least amount of unfamiliar abstractions.</p>
<p>Start by downloading
<a href="https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/nacl_sdk.zip">nacl_sdk.zip</a>
and unzipping it in a directory where it can stay. We will need to refer into this directory.
nacl_sdk is merely a tool for downloading the real sdk, so you need to ask it to do so:</p>
<style>
.pygments.file {
background: none;
}
pre.file {
border-left: 1px solid #ccc;
padding: 0px 20px 10px 20px;
}
.file::before {
font-family: normal;
display: block;
content: attr(data-filename);
background: #f8f8f8;
padding: 3px 8px 4px 8px;
margin-bottom: 10px;
margin-left: -20px;
margin-right: -20px;
}
.note {
font-style: italic;
}
</style>
<pre class="pygments file" data-filename="bash snippet">
curl -O <span class="s1">'https://storage.googleapis.com/nativeclient-mirror/nacl/nacl_sdk/nacl_sdk.zip'</span>
unzip nacl_sdk.zip
<span class="nb">cd </span>nacl_sdk
./naclsdk update <span class="c"># Downloads the real SDK. This takes a while</span>
<span class="c"># We can see that naclsdk installed pepper_nn, and we need to know about this directory:</span>
<span class="nv">PEPPER</span><span class="o">=</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span>/pepper_* <span class="c"># Keep this path for later :)</span>
</pre>
<p>You might consult the <a href="https://developers.google.com/native-client/dev/sdk/download">official
documentation of the naclsdk tool</a> to see how you can use it for staying up to date and so on, if
that interests you.</p>
<p>Now, let's start our own project. You need source code:</p>
<pre class="pygments file" data-filename="main.cpp">
<span class="cp">#include <ppapi_simple/ps_main.h></span>
<span class="c1">// The entry point function can have any name:</span>
<span class="kt">int</span> <span class="nf">ppapi_simple_main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// ... but we need to tell ppapi_simple about it:</span>
<span class="n">PPAPI_SIMPLE_REGISTER_MAIN</span><span class="p">(</span><span class="n">ppapi_simple_main</span><span class="p">)</span>
</pre>
<p>Compile this with <code>pnacl-clang++</code>:</p>
<pre class="pygments file" data-filename="bash snippet">
<span class="c"># First, you need to fill in this stuff:</span>
<span class="nv">PEPPER</span><span class="o">=</span><span class="note">Path to the SDK. It should end with nacl_sdk/pepper_n, for some number n</span>
<span class="nv">PLATFORM</span><span class="o">=</span><span class="note">The platform bit of the directories named <span class="nv">$PEPPER</span>/toolchain/<span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span>_pnacl</span>
<span class="nv">BINDIR</span><span class="o">=</span><span class="note">"bin" or "bin64", matching <span class="nv">$PEPPER</span>/toolchain/<span class="k">${</span><span class="nv">PLATFORM</span><span class="k">}</span>_pnacl/<span class="nv">$BINDIR</span></span>
<span class="nv">BUILD_VARIANT</span><span class="o">=</span><span class="note">"Release" or "Debug", used to select variants of the ppapi libraries</span>
<p><span class="c"># Locate the stuff we need inside nacl_sdk:</span>
<span class="nv">TOOLCHAIN</span><span class="o">=</span><span class="s2">"${PEPPER}/toolchain/${PLATFORM}_pnacl/${BINDIR}"</span></p>
<p><span class="nv">CXX</span><span class="o">=</span><span class="s2">"$TOOLCHAIN/pnacl-clang++"</span>
<span class="nv">LINK</span><span class="o">=</span><span class="s2">"$CXX"</span>
<span class="nv">FINALIZE</span><span class="o">=</span><span class="s2">"$TOOLCHAIN/pnacl-finalize"</span>
<span class="c"># For other tools, like ar and ranlib, you must also</span>
<span class="c"># use the pnacl-prefixed ones in this directory</span></p>
<p><span class="nv">PPAPI_INCLUDE</span><span class="o">=</span><span class="s2">"$PEPPER/include"</span>
<span class="nv">PPAPI_LIBDIR</span><span class="o">=</span><span class="s2">"$PEPPER/lib/pnacl/$BUILD_VARIANT"</span></p>
<p><span class="c"># And now we can compile:</span>
<span class="nv">$CXX</span> -c -o main.o -pthread -I <span class="s2">"$PPAPI_INCLUDE"</span> main.cpp</p>
<p><span class="c"># Link:</span>
<span class="nv">LIBS</span><span class="o">=</span><span class="s2">"-lppapi_simple -lppapi -lppapi_cpp -lnacl_io"</span>
<span class="nv">$LINK</span> -o minimal_unstripped.bc -pthread -L <span class="s2">"$PPAPI_LIBDIR"</span> main.o <span class="nv">$LIBS</span></p>
<p><span class="c"># Finalize. This step doesn't have an analogy in regular build procedures.</span>
<span class="c"># The --help text says "This tool prepares a PNaCl bitcode application for ABI stability."</span>
<span class="nv">$FINALIZE</span> -o minimal.pexe minimal_unstripped.bc
</pre></p>
<p>The pnacl module can not be loaded directly with an <code><embed></code> tag.
Instead, it must be indirectly loaded via a
<a href="https://developers.google.com/native-client/dev/reference/nacl-manifest-format">manifest file</a>:</p>
<pre class="pygments file" data-filename="minimal.nmf">
<span class="p">{</span>
<span class="nt">"program"</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">"portable"</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">"pnacl-translate"</span><span class="p">:</span> <span class="p">{</span>
<span class="nt">"url"</span><span class="p">:</span> <span class="s2">"minimal.pexe"</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</pre>
<pre class="pygments file" data-filename="index.html">
<span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head><title></span>Actually pnacl<span class="nt"></title></head></span>
<span class="nt"><body></span>
<span class="nt"><embed</span>
<span class="na">width=</span><span class="s">"200"</span> <span class="na">height=</span><span class="s">"200"</span>
<span class="na">src=</span><span class="s">"minimal.nmf"</span>
<span class="na">type=</span><span class="s">"application/x-pnacl"</span>
<span class="nt">></embed></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</pre>
<p>Web browsers will not accept the <code><embed></code> on a <code>file:</code>-URL, so
you need to serve this via HTTP, for example by using <code>python -m SimpleHTTPServer</code>.</p>
<p>You can now actually load this page in Chrome and have the code be compiled to machine code (this
can take several seconds) and executed. When it is finished, the message <code>NativeClient: NaCl
module crashed</code> will appear in the debug console. This is as expected, because Google doesn't
really want PNaCl modules to terminate at all, so they confusingly call this situation a crash.</p>
<p>Portable Native Client requires compilation to machine code before the module can be executed.
They call this process "translation". Chrome caches the translated modules rather aggressively,
so if it seems that your new changes don't take effect, you can start the HTTP server on a different
port to bypass the cache, for example with <code>python -m SimpleHTTPServer 8001</code>.</p>
<p>Hopefully, this is enough to let you set up a proper build environment for developing PNaCl modules
or even integrating it into your existing build systems. Keep on reading if you want to actually
do stuff with this module.</p>
<h2>Basic interaction with the browser</h2>
<p>Let's build up some simple interaction with the browser environment. There is a message passing
mechanism with the basic Pepper API that we could use directly. However, we will leverage the
wrapping they have done for us in ppapi_simple, which lets us pipe standard output from the module
into this message passing functionality. The <code>ps_stdout</code> and <code>ps_tty_prefix</code>
attributes on the <code><embed></code> tag are required for this to work.</p>
<p>We will also pass arguments into the module. In general, the PNaCl module can read all the
attributes of the <code><embed></code> tag. Additionally, pepper_simple reads the ones called
<code>arg<i>n</i></code> and passes them to our main-function as regular command line arguments.</p>
<pre class="pygments file" data-filename="index.html"><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head><title></span>Actually pnacl<span class="nt"></title></head></span>
<span class="nt"><body></span>
<span class="nt"><embed</span>
<span class="na">id=</span><span class="s">"pnacl"</span>
<span class="na">width=</span><span class="s">"200"</span> <span class="na">height=</span><span class="s">"200"</span>
<span class="na">src=</span><span class="s">"minimal.nmf"</span>
<span class="na">type=</span><span class="s">"application/x-pnacl"</span>
<span class="na">ps_stdout=</span><span class="s">"dev/tty"</span>
<span class="na">ps_tty_prefix=</span><span class="s">""</span>
<span class="na">arg0=</span><span class="s">"arrrr"</span>
<span class="nt">></embed></span>
<span class="nt"><pre</span> <span class="na">id=</span><span class="s">"output"</span><span class="nt">></pre></span>
<span class="nt"><script></span>
<span class="kd">var</span> <span class="nx">pnacl</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"pnacl"</span><span class="p">);</span>
<span class="kd">function</span> <span class="nx">log_handler</span><span class="p">(</span><span class="nx">tag</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">args</span> <span class="o">=</span> <span class="p">[</span><span class="nx">tag</span><span class="p">].</span><span class="nx">concat</span><span class="p">(</span><span class="nb">Array</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">slice</span><span class="p">.</span><span class="nx">call</span><span class="p">(</span><span class="nx">arguments</span><span class="p">));</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">.</span><span class="nx">apply</span><span class="p">(</span><span class="nx">console</span><span class="p">,</span> <span class="nx">args</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">// Dump data for <a href="http://www.w3.org/TR/progress-events/">Progress Events</a>:</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'loadstart'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"loadstart"</span><span class="p">));</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'progress'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"progress"</span><span class="p">));</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'load'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"load"</span><span class="p">));</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'error'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"error"</span><span class="p">));</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'abort'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"abort"</span><span class="p">));</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'loadend'</span><span class="p">,</span> <span class="nx">log_handler</span><span class="p">(</span><span class="s2">"loadend"</span><span class="p">));</span>
<span class="c1">// The 'error' and 'abort' events get a descriptive error message:</span>
<span class="kd">function</span> <span class="nx">error_handler</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">error</span><span class="p">(</span><span class="s2">"Error occurred:"</span><span class="p">,</span> <span class="nx">pnacl</span><span class="p">.</span><span class="nx">lastError</span><span class="p">);</span>
<span class="p">}</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'error'</span><span class="p">,</span> <span class="nx">error_handler</span><span class="p">);</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'abort'</span><span class="p">,</span> <span class="nx">error_handler</span><span class="p">);</span>
<span class="c1">// Handle Native Client-specific events:</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'message'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">ev</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">"output"</span><span class="p">);</span>
<span class="nx">output</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">+=</span> <span class="nx">ev</span><span class="p">.</span><span class="nx">data</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">pnacl</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="s1">'crash'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s2">"Exit code:"</span><span class="p">,</span> <span class="nx">pnacl</span><span class="p">.</span><span class="nx">exitStatus</span><span class="p">);</span>
<span class="p">});</span>
<span class="nt"></script></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</pre>
<p>And make the module do slightly more interesting stuff:</p>
<pre class="pygments file" data-filename="main.cpp"><span class="cp">#include <iostream></span>
<span class="cp">#include <unistd.h></span>
<span class="cp">#include <ppapi_simple/ps_main.h></span>
<span class="kt">int</span> <span class="nf">ppapi_simple_main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">*</span> <span class="n">argv</span><span class="p">[])</span> <span class="p">{</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="n">i</span><span class="o"><</span><span class="n">argc</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o"><<</span> <span class="s">"Argument "</span> <span class="o"><<</span> <span class="n">i</span> <span class="o"><<</span> <span class="s">": "</span> <span class="o"><<</span> <span class="n">argv</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o"><<</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">std</span><span class="o">::</span><span class="n">cerr</span> <span class="o"><<</span> <span class="s">"Standard error output appears in the debug console</span><span class="se">\n</span><span class="s">"</span><span class="p">;</span>
<span class="c1">// Since PNaCl modules aren't supposed to terminate, we need to give</span>
<span class="c1">// Pepper some breathing room to convey all the messages:</span>
<span class="n">sleep</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">PPAPI_SIMPLE_REGISTER_MAIN</span><span class="p">(</span><span class="n">ppapi_simple_main</span><span class="p">)</span>
</pre>
<p>Compile, link and finalize as before, then reload the page in your browser. Be sure to have the
debug console open. Hopefully, this should be enough to get you started with PNaCl modules that
actually do something.</p>
<h2>Further reading</h2>
<p>Why don't you try and do something with the <a href="https://developers.google.com/native-client/dev/peppercpp/classpp_1_1_graphics2_d">
2D graphics API</a>? Hot tip: You can get an <code>InstanceHandle</code> in your pepper_simple
module like this: <code>pp::InstanceHandle(PSInstance::GetInstance())</code></p>
<p>Get deeper into the core of the Pepper API by peeling away pepper_simple and implementing your
stuff directly on the C++ (or C) API. Read the <a href="https://chromium.googlesource.com/experimental/chromium/src/+/refs/heads/master/native_client_sdk/src/libraries/ppapi_simple">
source code of pepper_simple</a> and transform your own module to work directly with ppapi/cpp or
have a go at the <a href="https://developers.google.com/native-client/dev/devguide/tutorial/tutorial-part1">
official tutorial</a>, which doesn't use ppapi_simple.</p>
<p>Want to load a resource over HTTP in your module? <code>iurlstream</code> will let you load
URLs in a backwards compatible blocking fashion, see
<a href="https://bitbucket.org/maghoff/snygg/src/tip/src/platform-pnacl/iurlstream.hpp">iurlstream.hpp</a>,
<a href="https://bitbucket.org/maghoff/snygg/src/tip/src/platform-pnacl/iurlstream.cpp">iurlstream.cpp</a>,
<a href="https://bitbucket.org/maghoff/snygg/src/tip/src/platform-pnacl/urlbuf.hpp">urlbuf.hpp</a> and
<a href="https://bitbucket.org/maghoff/snygg/src/tip/src/platform-pnacl/urlbuf.cpp">urlbuf.cpp</a>.
</p>
<p>Happy hacking!</p>
PlainTalk: a simple network protocol syntaxhttps://magnushoff.com/plaintalkMon, 02 Dec 2013 00:00:00 +0000<p>PlainTalk: a simple network protocol syntax</p>
Asmtut 6: Live interactionhttps://magnushoff.com/blog/asmtut-6/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p>Since the last time, the nasm people have been able to fix the bugs I stumbled upon, and pushed them in <a href="http://www.nasm.us/pub/nasm/releasebuilds/2.10.06/macosx/nasm-2.10.06-macosx.zip">a release</a>.</p>
<p>Anything newer than 2.10.6 is probably also fine. Please note that you have to update if you are following this tutorial on OS X, because we will be triggering the bugs present in earlier versions.</p>
<p>With that out of the way, let's dive into making the game real time, so you don't have to hold down buttons on the keyboard. This should serve as a good repetition after a long hiatus, because we will only be doing more of the stuff we have already learned.</p>
<h2 id="step-13-ansi-escape-codes">Step 13: ANSI escape codes</h2>
<p>This part is for you, Michael!</p>
<p>When playing the latest version of the game we are making, we would fill the scrollback buffer in the terminal quite quickly with uninteresting prints of former game states. What we want to do instead is to print new versions of the board on top of the previous version. To do this, we will use an <a href="http://en.wikipedia.org/wiki/ANSI_escape_code">ANSI escape code</a> sequence to move the cursor up to the top of the board each time we print it.</p>
<p>We want to use the CSI code CUU; Cursor up. The structure for CUU is <em>ESC</em>, '[', number of rows to move upwards, 'A'. <em>ESC</em> is the magic value 27 = 0x1b = 033. The number of rows we want to move is <em>height + 2</em>, counting the top and bottom solid lines in addition to the <em>height</em> number of open lines:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">move_up </span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0x1b</span><span style="color:#323232;">, </span><span style="color:#183691;">'[27A'</span><span style="font-style:italic;color:#969896;"> ; Keep number in string equal to height + 2
</span></code></pre>
<p>We can put this right above our definition of <em>board</em>, and then print the entire thing in one syscall. We also need the size. Let's put this calculation right under <em>board_size</em>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">move_up_then_board_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$</span><span style="color:#323232;">-</span><span style="font-weight:bold;color:#795da3;">move_up
</span></code></pre>
<p>If we print both of <em>move_up</em> and <em>board</em> instead of just <em>board</em>, it will finally stand still and not scroll back:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">move_up</span><span style="font-style:italic;color:#969896;"> ; buf
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">move_up_then_board_size</span><span style="font-style:italic;color:#969896;"> ; nbyte
</span></code></pre>
<p>In addidtion, we need to print it once without <em>move_up</em> before entering <em>main_loop</em>, so we avoid overwriting the user's terminal history. This is easily accomplished by sticking an additional <em>syscall</em> to <em>SYS_write</em> just before <em>main_loop</em>.</p>
<p>Your program should now look like <a href="/asmtut/lesson06/part1.asm">part1.asm</a>.</p>
<h2 id="step-14-velocity">Step 14: Velocity</h2>
<p>Next we will get some velocity going, so that the snake keeps moving in the direction it's going. We will do this by replacing the <em>inc</em>, <em>dec</em>, <em>add</em> and <em>sub</em> instructions in our <em>cmp</em>/<em>jne</em> block by storing the appropriate velocity in <em>r15</em> with appropriate <em>mov r15, <imm></em> instructions. We'll put in <em>add r8, r15</em> just below <em>.main_loop:</em>. The additional details I'll leave as an excercise :)</p>
<p>Hopefully, you will end up with something like <a href="/asmtut/lesson06/part2.asm">part2.asm</a>.</p>
<h2 id="step-15-nonblocking-input">Step 15: Nonblocking input</h2>
<p>Even though we are in half-cooked (sigh) mode, we are still in blocking mode. The <em>SYS_read</em> call blocks until it can read at least one byte. Let's finally change that. We will do that by setting and unsetting the <em>O_NONBLOCK</em> flag on <em>STDIN_FILENO</em> with <em>SYS_fcntl</em>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">SYS_fcntl </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000000 </span><span style="color:#323232;">+ </span><span style="color:#0086b3;">92
</span><span style="font-weight:bold;color:#795da3;">F_SETFL </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x00000004
</span><span style="font-weight:bold;color:#795da3;">O_NONBLOCK </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x00000004
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="font-weight:bold;color:#795da3;">SYS_fcntl
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="font-weight:bold;color:#795da3;">STDIN_FILENO
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">F_SETFL
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">O_NONBLOCK</span><span style="font-style:italic;color:#969896;"> ; 0 to unset
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>Unfortunately, even though we set this flag specifically on <em>STDIN_FILENO</em>, it magically applies to standard out as well. We don't need or want to handle nonblocking output, so we opt for the cheap workaround of setting and unsetting <em>O_NONBLOCK</em> before and after calls to <em>SYS_read</em>. We'll set the flag just before <em>.read_more:</em> and unset it between <em>.done:</em> and <em>jmp .main_loop</em>. Running the game now, it seems to still be kind of blocking, at least until we press one key. This is because we currently do not attempt to tell the difference between having read one byte and not. Let's fix that.</p>
<p>Begin by removing the "Assume exactly one byte was read"-comment. In <em>man 2 read</em> we see that the count of bytes read will be returned, and from the all-mighty ABI we learn that such a return value will be passed in <em>rax</em>. When no bytes are read because of <em>O_NONBLOCK</em>, <em>SYS_read</em> will actually return -1 and claim that the error <em>EAGAIN</em> has occurred. The simplest conclusion we can draw from this is that we check <em>rax</em> and jump to <em>.done</em> if it's not 1:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">cmp </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#a71d5d;">jne </span><span style="font-weight:bold;color:#795da3;">.done
</span></code></pre>
<p>Your game should now be incredibly unplayable, and look something like <a href="/asmtut/lesson06/part3.asm">part3.asm</a>.</p>
<h2 id="step-16-real-time">Step 16: Real time</h2>
<p>At this point we need some kind of real time game loop. However, it is easier to cheat and just sleep for a bit between each frame. The price we have to pay for going the easy route is that we have to implement <em>sleep</em> in terms of <em>select</em>, since <em>sleep</em> is not available directly as a syscall.</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">SYS_select </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000000 </span><span style="color:#323232;">+ </span><span style="color:#0086b3;">93
</span></code></pre>
<p>From <em>man select</em>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">int </span><span style="font-weight:bold;color:#795da3;">select</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="color:#323232;">nfds, fd_set</span><span style="font-weight:bold;color:#a71d5d;">* </span><span style="color:#323232;">readfds, fd_set</span><span style="font-weight:bold;color:#a71d5d;">* </span><span style="color:#323232;">writefds, fd_set</span><span style="font-weight:bold;color:#a71d5d;">* </span><span style="color:#323232;">errorfds, </span><span style="font-weight:bold;color:#a71d5d;">struct</span><span style="color:#323232;"> timeval</span><span style="font-weight:bold;color:#a71d5d;">* </span><span style="color:#323232;">timeout);
</span></code></pre>
<p><em>nfds</em>, <em>readfds</em>, <em>writefds</em>, and <em>errorfds</em> can all be 0. That should fill up rdi, rsi, rdx and rcx. <em>timeout</em> must point to a valid <em>timeval</em> struct instance, its address placed in <em>r8</em>. Oh, nuts. Since we are already using <em>r8</em>, this is bad news. Let's just replace all instances of <em>r8</em> with <em>r14</em> to avert immediate disaster.</p>
<p>The declaration of <em>struct timeval</em> is messy and ridden with preprocessor noise, but it boils down to this nasm:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">struc </span><span style="font-weight:bold;color:#795da3;">timeval</span><span style="font-style:italic;color:#969896;">
; resq: reserve quadword (64 bits)
</span><span style="font-weight:bold;color:#795da3;">.tv_sec: </span><span style="color:#62a35c;">resq </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">.tv_nsec: </span><span style="color:#62a35c;">resq </span><span style="color:#0086b3;">1
</span><span style="color:#62a35c;">endstruc
</span></code></pre>
<p>Now we get to exercise nasm's syntax for defining an instance of a struct in the data section:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">timeout:
</span><span style="color:#62a35c;">istruc </span><span style="font-weight:bold;color:#795da3;">timeval
</span><span style="color:#62a35c;">at </span><span style="font-weight:bold;color:#795da3;">timeval.tv_sec</span><span style="color:#323232;">, </span><span style="color:#62a35c;">dq </span><span style="color:#0086b3;">0
</span><span style="color:#62a35c;">at </span><span style="font-weight:bold;color:#795da3;">timeval.tv_nsec</span><span style="color:#323232;">, </span><span style="color:#62a35c;">dq </span><span style="color:#0086b3;">100000
</span><span style="color:#62a35c;">iend
</span></code></pre>
<p>Then we're all set for adding the sleep call, right before <em>.read_more:</em>, so we can read any keypresses that happened during the sleep:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="font-weight:bold;color:#795da3;">SYS_select
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; nfds
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; readfds
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; writefds
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rcx, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; errorfds
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">r8, </span><span style="font-weight:bold;color:#795da3;">timeout</span><span style="font-style:italic;color:#969896;"> ; timeout
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>Your code should now approximate <a href="/asmtut/lesson06/part4.asm">part4.asm</a>.</p>
<p>And playing the game should look kind of like this:</p>
<style>.embed-container { position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; } .embed-container iframe, .embed-container object, .embed-container embed { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }</style><div class='embed-container'><iframe src='https://www.youtube.com/embed/UnjGoughmH0' frameborder='0' allowfullscreen></iframe></div>
<p><em>Next lesson: TBA</em></p>
Asmtut 5: More snappy interactionhttps://magnushoff.com/blog/asmtut-5/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p>At this point, we are tired of the line-buffered interface. Let's make it more responsive!</p>
<h2 id="step-11-cleanup">Step 11: Cleanup</h2>
<p>First, let's just clean up our code a bit by putting our constants on top:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">; System defines:
</span><span style="font-weight:bold;color:#795da3;">SYS_write </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000004
</span><span style="font-weight:bold;color:#795da3;">SYS_read </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000003
</span><span style="font-weight:bold;color:#795da3;">SYS_exit </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000001
</span><span style="font-weight:bold;color:#795da3;">STDIN_FILENO </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0
</span><span style="font-weight:bold;color:#795da3;">STDOUT_FILENO </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">1
</span></code></pre>
<p>Substitute the values in your code accordingly.</p>
<p>You can give the program constants and macros a descriptive header, too: <em>; Program defines:</em> Both of these blocks can go above the <em>.data</em> section.</p>
<p>Additionally, <em>input_char</em> is a bit weird, because we are specifying a value we are never actually reading. This dummy value is stored in the compiled executable, wasting a whole byte. In general, it is better to specify data where we don't care about the initial value in the <em>.bss</em> section. Here, you only reserve the necessary number of bytes you need for the data, rather than specifying the data, so instead of <em>db</em> we use <em>resb</em> for reserving bytes:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">section </span><span style="font-weight:bold;color:#795da3;">.bss
input_char </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span></code></pre>
<p>Back in the old DOS days, this memory would be uninitialized. It would just contain whatever was left there by previously running processes. That allows leakage of potentially sensitive information, so in modern operating systems this memory gets zero initialized by the OS. The name of this section is an historical accident <a href="http://en.wikipedia.org/wiki/.bss">detailed on Wikipedia</a>.</p>
<p>We have now cleaned up everything so it looks something like <a href="/asmtut/lesson05/part1.asm">part1.asm</a>.</p>
<h2 id="step-12-ioctl-structs-and-bitmasks">Step 12: ioctl: Structs and bitmasks</h2>
<p>We need to disable echoing and line buffering for standard in. We are going to implement something like the C versions here: <a href="http://www.glue.umd.edu/afs/glue.umd.edu/system/info/olh/Programming/Answers_to_Common_Questions_about_C/c_getch">http://www.glue.umd.edu/afs/glue.umd.edu/system/info/olh/Programming/Answers_to_Common_Questions_about_C/c_getch</a> and <a href="http://www.glue.umd.edu/afs/glue.umd.edu/system/info/olh/Programming/Answers_to_Common_Questions_about_C/c_terminal_echo">http://www.glue.umd.edu/afs/glue.umd.edu/system/info/olh/Programming/Answers_to_Common_Questions_about_C/c_terminal_echo</a></p>
<p>We will need <a href="http://www.freebsd.org/cgi/man.cgi?query=ioctl&sektion=2"><em>ioctl</em></a>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">SYS_ioctl </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x02000000 </span><span style="color:#323232;">+ </span><span style="color:#0086b3;">54
</span></code></pre>
<p>The first argument is the filedescriptor we want to control. <em>STDIN_FILENO</em> should be just what the doctor ordered.</p>
<p>The second argument is the <em>request</em> for <em>ioctl</em>. It specifies wihch underlying functionality we are actually looking for. It looks like <em>ioctl</em> is just an indirection step. The <em>request</em> values we need are symbolically <em>TIOCGETP</em> and <em>TIOCSETP</em>, and they seem to be defined in /usr/include/sys/ioctl_compat.h:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">#define TIOCGETP _IOR(</span><span style="color:#183691;">'t'</span><span style="color:#323232;">, </span><span style="color:#0086b3;">8</span><span style="color:#323232;">,</span><span style="font-weight:bold;color:#795da3;">struct sgttyb)/</span><span style="color:#323232;">* </span><span style="font-weight:bold;color:#795da3;">get parameters </span><span style="color:#323232;">-- </span><span style="font-weight:bold;color:#795da3;">gtty </span><span style="color:#323232;">*</span><span style="font-weight:bold;color:#795da3;">/
#define TIOCSETP _IOW(</span><span style="color:#183691;">'t'</span><span style="color:#323232;">, </span><span style="color:#0086b3;">9</span><span style="color:#323232;">,</span><span style="font-weight:bold;color:#795da3;">struct sgttyb)/</span><span style="color:#323232;">* </span><span style="font-weight:bold;color:#795da3;">set parameters </span><span style="color:#323232;">-- </span><span style="font-weight:bold;color:#795da3;">stty </span><span style="color:#323232;">*</span><span style="font-weight:bold;color:#795da3;">/
</span></code></pre>
<p>The easiest way to evaluate these C macros is to make a tiny C program assigning these values to local variables and disassemble the result. Or print the values to standard output if you prefer. I found these values:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">TIOCGETP </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x40067408
</span><span style="font-weight:bold;color:#795da3;">TIOCSETP </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x80067409
</span></code></pre>
<p>When using either of these with <em>ioctl</em>, we need to supply a <em>struct sgttyb*</em> as the third argument. Pointers are easy; that's just the same thing as an address. But what about the struct? Let's look at its definition in the same header file:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">struct </span><span style="color:#323232;">sgttyb {
</span><span style="font-weight:bold;color:#a71d5d;">char</span><span style="color:#323232;"> sg_ispeed; </span><span style="font-style:italic;color:#969896;">/* input speed */
</span><span style="font-weight:bold;color:#a71d5d;">char</span><span style="color:#323232;"> sg_ospeed; </span><span style="font-style:italic;color:#969896;">/* output speed */
</span><span style="font-weight:bold;color:#a71d5d;">char</span><span style="color:#323232;"> sg_erase; </span><span style="font-style:italic;color:#969896;">/* erase character */
</span><span style="font-weight:bold;color:#a71d5d;">char</span><span style="color:#323232;"> sg_kill; </span><span style="font-style:italic;color:#969896;">/* kill character */
</span><span style="font-weight:bold;color:#a71d5d;">short</span><span style="color:#323232;"> sg_flags; </span><span style="font-style:italic;color:#969896;">/* mode flags */
</span><span style="color:#323232;">};
</span></code></pre>
<p>While the precise meaning of this is platform defined, it is not hard to guess correctly what it boils down to. Each <em>char</em> is one byte, this is actually well defined, and the <em>short</em> is two bytes. In assembly-speak on x86, a 16 bit value is called a word, so a <em>short</em> is a <em>word</em>. Additionally, we have to consider that the values might be aligned to 32 bit addresses or something. The rules for this are defined in the ABI, but it is much quicker to test it in C and disassemble again :) The values are packed densely, so an instance of this struct translates to:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">sgttyb_instance:
sg_ispeed </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">sg_ospeed </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">sg_erase </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">sg_kill </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">sg_flags </span><span style="color:#62a35c;">resw </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; Note the use of _reserve word_ here
</span><span style="font-weight:bold;color:#795da3;">sgttyb_instance_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$</span><span style="color:#323232;">-</span><span style="font-weight:bold;color:#795da3;">sgttyb_instance
</span></code></pre>
<p>Now, we could give in the address of <em>sgttyb_instance</em>, and stuff would work!</p>
<p>However, let's abstract things up a notch and use NASM's built-in macros for working with structs. We can define a structure rather than just an instance of it, with <a href="http://www.nasm.us/doc/nasmdoc4.html#section-4.12.10"><em>struc</em> and <em>endstruc</em></a>:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">struc </span><span style="font-weight:bold;color:#795da3;">sgttyb
.sg_ispeed: </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">.sg_ospeed: </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">.sg_erase: </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">.sg_kill: </span><span style="color:#62a35c;">resb </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">.sg_flags: </span><span style="color:#62a35c;">resw </span><span style="color:#0086b3;">1
</span><span style="color:#62a35c;">endstruc
</span></code></pre>
<p>This can go in the <em>System defines</em>-block, since it doesn't define or reserve any data. Instead, it gives us seven new symbols to work with; First, the offsets of each struct member:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">; The struc-declaration above gives us these:
</span><span style="font-weight:bold;color:#795da3;">sgttyb.sg_ispeed </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0
</span><span style="font-weight:bold;color:#795da3;">sgttyb.sg_ospeed </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">1
</span><span style="font-weight:bold;color:#795da3;">sgttyb.sg_erase </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">2
</span><span style="font-weight:bold;color:#795da3;">sgttyb.sg_kill </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">3
</span><span style="font-weight:bold;color:#795da3;">sgttyb.sg_flags </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">4
</span></code></pre>
<p>The size of the struct:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">sgttyb_size </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">6
</span></code></pre>
<p>And finally, just to make things work properly for NASM:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">sgttyb </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; This one is not interesting
</span></code></pre>
<p>So now, we can reserve the correct number of bytes for one <em>sgttyb</em> instance in the <em>.bss</em> section:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">state </span><span style="color:#62a35c;">resb </span><span style="font-weight:bold;color:#795da3;">sgttyb_size</span><span style="font-style:italic;color:#969896;"> ; Doesn't work in NASM 2.10.05 and earlier
</span></code></pre>
<p>Unfortunately, due to <a href="http://bugzilla.nasm.us/show_bug.cgi?id=3392231">a bug in NASM</a>, we need to allocate more space:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">state </span><span style="color:#62a35c;">resb </span><span style="font-weight:bold;color:#795da3;">sgttyb_size </span><span style="color:#323232;">* </span><span style="color:#0086b3;">2</span><span style="font-style:italic;color:#969896;"> ; *2 to work around bug :(
</span></code></pre>
<p>We can finally do the syscall:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="font-weight:bold;color:#795da3;">SYS_ioctl
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="font-weight:bold;color:#795da3;">STDIN_FILENO
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">TIOCGETP
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">state
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>If we assume that the syscall succeeded, we can jump right into manipulating <em>sg_flags</em>. We want to turn off echoing and line-buffering. Disabling echoing is done by turning off the ECHO flag. Disabling line-buffering is done by enabling "half-cooked" mode or turning on the CBREAK flag. You can read about the <a href="http://en.wikipedia.org/wiki/Cooked_mode">crazy naming on Wikipedia</a>.</p>
<p>ECHO and CBREAK are defined in ioctl_compat.h:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">CBREAK </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x00000002</span><span style="font-style:italic;color:#969896;"> ; half-cooked mode
</span><span style="font-weight:bold;color:#795da3;">ECHO </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">0x00000008</span><span style="font-style:italic;color:#969896;"> ; echo input
</span></code></pre>
<p>Now, we need to do the assembly equivalent of the C code:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">state.sg_flags &= ~ECHO</span><span style="font-style:italic;color:#969896;">;
</span><span style="font-weight:bold;color:#795da3;">state.sg_flags |= CBREAK</span><span style="font-style:italic;color:#969896;">;
</span></code></pre>
<p>Even though it is not strictly necessary, we are going to load <em>sg_flags</em> into a register to work with it, instead of working directly on memory:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">ax, [</span><span style="font-weight:bold;color:#795da3;">state </span><span style="color:#323232;">+ </span><span style="font-weight:bold;color:#795da3;">sgtty.sg_flags</span><span style="color:#323232;">]
</span></code></pre>
<p>Now, fundamental arithmetics is pretty straightforward:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">and </span><span style="color:#323232;">ax, </span><span style="font-weight:bold;color:#795da3;">~ECHO</span><span style="font-style:italic;color:#969896;"> ; ax &= ~ECHO -- ~ECHO is evaluated by NASM
</span><span style="font-weight:bold;color:#a71d5d;">or </span><span style="color:#323232;">ax, </span><span style="font-weight:bold;color:#795da3;">CBREAK</span><span style="font-style:italic;color:#969896;"> ; ax |= CBREAK
</span></code></pre>
<p>And then, store it back into the struct:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">[</span><span style="font-weight:bold;color:#795da3;">state </span><span style="color:#323232;">+ </span><span style="font-weight:bold;color:#795da3;">sgtty.sg_flags</span><span style="color:#323232;">], ax
</span></code></pre>
<p>We are now ready to set this with ioctl:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="font-weight:bold;color:#795da3;">SYS_ioctl
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="font-weight:bold;color:#795da3;">STDIN_FILENO
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">TIOCSETP</span><span style="font-style:italic;color:#969896;"> ; SET this time around
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">state
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>This should all go in <em>main:</em>, before <em>.main_loop:</em>, as it serves as initializing code.</p>
<p>When you have done this, the game should be slightly more interactive since all you have to do now is press a key to make things happen. Note that even though it is no longer line buffered, it is still blocking, so you do need to press buttons. Next time we will make it non-blocking and real time!</p>
<p>You might end up with echoing disabled in your shell after running this. Be aware that you can reset your terminal to a good state with the command <em>reset</em>.</p>
<p>Your code should now look similar to <a href="/asmtut/lesson05/part2.asm">part2.asm</a>.</p>
<p><em>Exercise:</em> We should be good citizens and reset <em>sg_flags</em> to its initial value upon exit. Store the original <em>sg_flags</em> in a new variable and call <em>ioctl</em> to reset to this state in <em>.exit:</em></p>
<p>Solution available in <a href="/asmtut/lesson05/exercise.asm">exercise.asm</a>.</p>
<p><em>Next lesson:</em> <a href="https://magnushoff.com/blog/asmtut-6/">Asmtut 6: Live interaction</a></p>
Asmtut 4: Inputhttps://magnushoff.com/blog/asmtut-4/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p>Let's start drawing and moving around a snake. Before we can ask the user where he wants to move the snake, we need to have a snake:</p>
<h2 id="step-9-writing-into-an-array">Step 9: Writing into an array</h2>
<p>Our <code>board</code> is basically a <code>char[]</code> in C lingo, even though it doesn't have a type. Which brings me to typing: <code>board</code> is a <code>char[]</code> only because we treat it as one. This is weak typing at its finest :)</p>
<p>What we have to work with is the address of the start of our array. Again, if you ask the assembler, <code>board</code> is just a label which translates to an address during assembling/linking. But we know better; there is an array of bytes starting at that address. We can get to the <em>n</em>-th byte by taking the address <em>board + n</em>. Simple! Note that, unlike in C, taking <em>board + n</em> will get us the address <em>n</em> bytes higher than <code>board</code>, whereas in C this would depend on the type of <code>board</code>.</p>
<p>The snake will start in the middle of the board — this is well established snake dogma — at (40, 13), so we need the address <em>board + 40 + 13 * (width + 1)</em> . Why <em>(width + 1)</em>? Because each line in <code>board</code> is terminated by a newline after <em>width</em> bytes. Let's make a mental note that we are smudging up abstractions by keeping the game data stored in a serialized format ready for printing, before we continue by making this mistake more comfortable by introducing a constant: <em>pitch equ width + 1</em>.</p>
<p>Luckily, all the factors we need for this calculation are constants, so we can just present them to nasm for calculation. Let's shove them into the register <em>r8</em>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">r8, </span><span style="font-weight:bold;color:#795da3;">board </span><span style="color:#323232;">+ </span><span style="color:#0086b3;">40 </span><span style="color:#323232;">+ </span><span style="color:#0086b3;">13 </span><span style="color:#323232;">* </span><span style="font-weight:bold;color:#795da3;">pitch
</span></code></pre>
<p>Now we have the address to the center of the board in <em>r8</em>. To write to this address we need the following new syntax:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#62a35c;">byte </span><span style="color:#323232;">[r8], </span><span style="color:#183691;">'O'
</span></code></pre>
<p><em>byte</em> tells the assembler the operand size, the thing in brackets is the address to write to and the thing in quotes gets replaced by its ASCII value.</p>
<p>Stick these two lines in at the top of your code and marvel at how much more beautiful everything is with an O in the middle!</p>
<p>Your code should now look like <a href="/asmtut/lesson04/part1.asm">part1.asm</a>.</p>
<h2 id="step-10-read-and-interpret">Step 10: Read and interpret</h2>
<p>We will need an additional <em>syscall</em> to read from standard input. Same old, same old: <a href="http://linux.die.net/man/2/read"><code>man 2 read</code></a>, <code>SYS_read</code> is <code>3</code> and <code>STDIN_FILENO</code> is <code>0</code>. We do need a buffer to read into, and for now a one byte big buffer should do:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">; In data-section
</span><span style="font-weight:bold;color:#795da3;">input_char </span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;">
; In code-section, below the write-call:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000003</span><span style="font-style:italic;color:#969896;"> ; SYS_read
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; filedes = STDIN_FILENO = 0
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">input_char</span><span style="font-style:italic;color:#969896;"> ; buf
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; nbyte
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>At this point we should absolutely check the return value, which we get in <em>rax</em>. For a little while, though, we can use this check:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">; Assume exactly one byte was read
</span></code></pre>
<p>Nice. The byte we assume we have read is currently stuck at <em>input_char</em>, but we want it in a register to make it easier to work with:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">al, [</span><span style="font-weight:bold;color:#795da3;">input_char</span><span style="color:#323232;">]</span><span style="font-style:italic;color:#969896;"> ; Almost works!
</span></code></pre>
<p>Again, to combat code size bloat due to 64 bit addresses, AMD forbade 64 bit literal addresses and introduced instruction pointer relative addressing — addressing relative to the address at which the instruction starts. For us, this means we can use <em>mov al, [rel input_char]</em> and have it working or we can do the recommended thing and put <em>default rel</em> at the start of our source file somewhere, and the <em>rel</em> is automatic in the <em>mov</em> instruction. While we're at it, let's just shove in <em>bits 64</em> at the top as well, to make the file more self-describing.</p>
<p>The operand size is implied by using an 8 bit register.</p>
<p>Okay, now we need some control flow similar to what in C would look like:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(al </span><span style="font-weight:bold;color:#a71d5d;">== </span><span style="color:#183691;">'w'</span><span style="color:#323232;">) {
</span><span style="font-style:italic;color:#969896;">// Move up
</span><span style="color:#323232;">} </span><span style="font-weight:bold;color:#a71d5d;">else if </span><span style="color:#323232;">(al </span><span style="font-weight:bold;color:#a71d5d;">== </span><span style="color:#183691;">'s'</span><span style="color:#323232;">) {
</span><span style="font-style:italic;color:#969896;">// Move down
</span><span style="color:#323232;">} </span><span style="font-style:italic;color:#969896;">// etc
</span></code></pre>
<p>The closest thing we have to work with is called conditional branching, which makes it look more like:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">if </span><span style="color:#323232;">(al </span><span style="font-weight:bold;color:#a71d5d;">!= </span><span style="color:#183691;">'w'</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">goto</span><span style="color:#323232;"> not_up;
</span><span style="font-style:italic;color:#969896;">// Move up
</span><span style="font-weight:bold;color:#a71d5d;">goto</span><span style="color:#323232;"> done;
not_up</span><span style="font-weight:bold;color:#a71d5d;">:
if </span><span style="color:#323232;">(al </span><span style="font-weight:bold;color:#a71d5d;">!= </span><span style="color:#183691;">'s'</span><span style="color:#323232;">) </span><span style="font-weight:bold;color:#a71d5d;">goto</span><span style="color:#323232;"> not_down;
</span><span style="font-style:italic;color:#969896;">// Move down
</span><span style="font-weight:bold;color:#a71d5d;">goto</span><span style="color:#323232;"> done;
not_down</span><span style="font-weight:bold;color:#a71d5d;">:
</span><span style="font-style:italic;color:#969896;">// etc
</span><span style="color:#323232;">
done</span><span style="font-weight:bold;color:#a71d5d;">:
</span></code></pre>
<p>This translates to the following assembly:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">cmp </span><span style="color:#323232;">al, </span><span style="color:#183691;">'w'</span><span style="font-style:italic;color:#969896;"> ; Compare al to 'w'
</span><span style="font-weight:bold;color:#a71d5d;">jne </span><span style="font-weight:bold;color:#795da3;">.not_up</span><span style="font-style:italic;color:#969896;"> ; "Jump if not equal" to ".not_up"
; TODO: Move up
</span><span style="font-weight:bold;color:#a71d5d;">jmp </span><span style="font-weight:bold;color:#795da3;">.done</span><span style="font-style:italic;color:#969896;"> ; Unconditionally jump to/goto ".done"
</span><span style="font-weight:bold;color:#795da3;">.not_up:
</span><span style="font-weight:bold;color:#a71d5d;">cmp </span><span style="color:#323232;">al, </span><span style="color:#183691;">'s'</span><span style="font-style:italic;color:#969896;"> ; Compare al to 's'
</span><span style="font-weight:bold;color:#a71d5d;">jne </span><span style="font-weight:bold;color:#795da3;">.not_up</span><span style="font-style:italic;color:#969896;"> ; "Jump if not equal" to ".not_down"
; TODO: Move down
</span><span style="font-weight:bold;color:#a71d5d;">jmp </span><span style="font-weight:bold;color:#795da3;">.done</span><span style="font-style:italic;color:#969896;"> ; Unconditionally jump to/goto ".done"
</span><span style="font-weight:bold;color:#795da3;">.not_down:</span><span style="font-style:italic;color:#969896;">
; etc
</span><span style="font-weight:bold;color:#795da3;">.done:
</span></code></pre>
<p>In order to keep things neat and tidy, we use local labels. Labels that start with a . are local to the closest label that doesn't start with one, so if we want to, we can think of the labels as <em>main.not_up</em> and so on.</p>
<p>The <em>cmp</em> instruction compares its two operands by subtracting the right one from the left one and storing some facts about the result in the processor's <em>rflags</em> register. The different conditional branch instructions use these flags to determine if they should make the jump or not. The specific flag we are using here is the zero flag, <em>zf</em>. If <em>al</em> and <em>'w'</em> are equal, subtracting them should yield zero, and when a subtraction yields zero, <em>zf</em> is set. In fact, <em>jne</em> is just an alias for the <em>jnz</em> instruction; Jump if not zero. There is also <em>je</em> and <em>jz</em>, which makes the jump if the operands are equal.</p>
<p>We want four tests for 'w', 's', 'a', 'd', for up, down, left and right, respectively. Additionally we put in a test for 'q' and make it jump to <em>.exit</em> if detected. You get to do all of this without further instruction.</p>
<p>For the "TODO: Move *"-pieces of the code, we are going to update <em>r8</em> which points into the <code>board</code>. For this, I am going to introduce no less than four new instructions along with their C equivalents:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;">; Move up:
</span><span style="font-weight:bold;color:#a71d5d;">sub </span><span style="color:#323232;">r8, </span><span style="font-weight:bold;color:#795da3;">pitch</span><span style="font-style:italic;color:#969896;"> ; r8 -= pitch
; Move down:
</span><span style="font-weight:bold;color:#a71d5d;">add </span><span style="color:#323232;">r8, </span><span style="font-weight:bold;color:#795da3;">pitch</span><span style="font-style:italic;color:#969896;"> ; r8 += pitch
; Move left:
</span><span style="font-weight:bold;color:#a71d5d;">dec </span><span style="color:#323232;">r8</span><span style="font-style:italic;color:#969896;"> ; r8--
; Move right:
</span><span style="font-weight:bold;color:#a71d5d;">inc </span><span style="color:#323232;">r8</span><span style="font-style:italic;color:#969896;"> ; r8++
</span></code></pre>
<p>At <em>.done:</em>, make it <em>jmp .main_loop</em> and put <em>.main_loop:</em> at the instruction that writes the O into the board and voilà, you have a snake-ish game-ish thing with line buffered input. Yes, you need to pump the game loop by pressing enter all the time :)</p>
<p>I encourage you to go and explore what happens when you step outside the board in each direction.</p>
<p>Your code should now look like <a href="/asmtut/lesson04/part2.asm">part2.asm</a>.</p>
<p><em>Exercise:</em> Implement support for dying by checking if the new cell the snake walks into is empty. A good place to check is just before writing the O. If the target space is not a ' ', exit the program.</p>
<p>When the user enters input that does not move the snake, such as the enter key, you should avoid jumping back to <em>.main_loop</em> so you don't end up killing the player in those cases. Instead, just jump back to the <em>SYS_read</em> call.</p>
<p>Solution available in <a href="/asmtut/lesson04/exercise.asm">exercise.asm</a>.</p>
<p><em>Next lesson:</em> <a href="https://magnushoff.com/blog/asmtut-5/">Asmtut 5: More snappy interaction</a></p>
Asmtut 3: Redundancieshttps://magnushoff.com/blog/asmtut-3/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p>This time we will automate stuff with the assembler.</p>
<h2 id="step-6-string-length">Step 6: String length</h2>
<p>In our Hello world-implementation, we used a tedious and error prone mechanism to measure the length of the string: counting by brain. Let's <a href="http://www.smbc-comics.com/index.php?db=comics&id=2039">offload that to the computer</a>.</p>
<p>NASM doesn't realize that <em>hello_world</em> represents a string for us. For NASM it is just a label, and in fact we could have written <em>hello_world:</em> with a colon, just like the <em>main:</em>-label. However, <em>we</em> still know that it is a string, and we can calculate its length by adding a label after it:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">hello_world: </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"Hello World!"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">hello_world_end:
</span></code></pre>
<p>We tack the colon onto our labels to get rid of a warning from NASM.</p>
<p>Now, we can use the <a href="http://www.tortall.net/projects/yasm/manual/html/nasm-crit.html">Critical Expression</a> <code>hello_world_end - hello_world</code> to calculate the length of the string in the code:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">hello_world_end </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">hello_world
</span></code></pre>
<p>We can also define constants with the keyword <em>equ</em>:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">hello_world_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">hello_world_end </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">hello_world
</span></code></pre>
<p>Let's stick this in the <em>data</em> section right under <em>hello_world_end</em>, and later on use this as the string length:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">hello_world_size
</span></code></pre>
<p>Even though the constant is in the <em>data</em> section, it will not be stored in the assembled binary as data. It will instead be substituted by its value during assembly. Inspect this with <em>otool -d</em> and <em>otool -tv</em> as we did before.</p>
<p>Finally, we can get rid of <em>hello_world_end</em> by using the special symbol <em>$</em>, which in NASM evaluates to the address at the beginning of the line. This is all we need:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">hello_world </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"Hello World!"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">hello_world_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$ </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">hello_world
</span></code></pre>
<p>Your asm-file might now look like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">section </span><span style="font-weight:bold;color:#795da3;">.data
hello_world </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"Hello World!"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">hello_world_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$ </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">hello_world
section .text
</span><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000004</span><span style="font-style:italic;color:#969896;"> ; SYS_write
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; filedes = STDOUT_FILENO = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">hello_world</span><span style="font-style:italic;color:#969896;"> ; buf = The address of hello_world string
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="font-weight:bold;color:#795da3;">hello_world_size</span><span style="font-style:italic;color:#969896;"> ; nbyte
</span><span style="font-weight:bold;color:#a71d5d;">syscall
mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001</span><span style="font-style:italic;color:#969896;"> ; SYS_exit
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Exit status
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre><h2 id="step-7-repetition">Step 7: Repetition</h2>
<p>Let's print out a snake game board instead of the boring greeting. For this, we will need to repeat lots of stuff. Luckily, NASM has a <a href="http://www.tortall.net/projects/yasm/manual/html/nasm-pseudop.html">pseudo-instruction</a> for simple repetitions: <code>TIMES</code></p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">board:
</span><span style="color:#62a35c;">times </span><span style="color:#0086b3;">80 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">board_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$ </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">board
</span></code></pre>
<p>Printing <em>board</em> instead of <em>hello_world</em> should now give you a nice line of X-es.</p>
<p>Let's have four walls:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">board:</span><span style="font-style:italic;color:#969896;">
; North wall:
</span><span style="color:#62a35c;">times </span><span style="color:#0086b3;">80 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0x0a</span><span style="font-style:italic;color:#969896;">
; One line of west and east walls:
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">times </span><span style="color:#0086b3;">78 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">" "
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a</span><span style="font-style:italic;color:#969896;">
; Another line of west and east walls:
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">times </span><span style="color:#0086b3;">78 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">" "
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a</span><span style="font-style:italic;color:#969896;">
; South wall:
</span><span style="color:#62a35c;">times </span><span style="color:#0086b3;">80 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">board_size </span><span style="color:#62a35c;">equ </span><span style="font-weight:bold;color:#795da3;">$ </span><span style="color:#323232;">- </span><span style="font-weight:bold;color:#795da3;">board
</span></code></pre>
<p>Your source code might now look like <a href="/asmtut/lesson03/part2.asm">part2.asm</a>.</p>
<h2 id="step-8-macros">Step 8: Macros</h2>
<p>This is getting ridiculous to read and write. We already know how to get rid of the repeated numerical constant; Add "width equ 80", and substitute the numerical literals appropriately.</p>
<p>However, we have an additional ace up our sleeve to help with this explosion of lines: Macros.</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">width </span><span style="color:#62a35c;">equ </span><span style="color:#0086b3;">80
</span><span style="color:#62a35c;">%macro </span><span style="font-weight:bold;color:#795da3;">full_line </span><span style="color:#0086b3;">0
</span><span style="color:#62a35c;">times </span><span style="font-weight:bold;color:#795da3;">width </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">db </span><span style="color:#0086b3;">0x0a
</span><span style="color:#62a35c;">%endmacro
%macro </span><span style="font-weight:bold;color:#795da3;">hollow_line </span><span style="color:#0086b3;">0
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"
</span><span style="color:#62a35c;">times </span><span style="font-weight:bold;color:#795da3;">width</span><span style="color:#323232;">-</span><span style="color:#0086b3;">2 </span><span style="color:#62a35c;">db </span><span style="color:#183691;">" "
</span><span style="color:#62a35c;">db </span><span style="color:#183691;">"X"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span><span style="color:#62a35c;">%endmacro
</span></code></pre>
<p>These are of course also hideous, but they make the data definition slightly less so:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">board:
full_line
hollow_line
hollow_line
full_line
</span></code></pre>
<p>If you are anything like me, you are probably planning on applying <code>TIMES</code> to the macros. That doesn't work, but luckily <code>%rep</code> does:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">board:
full_line
</span><span style="color:#62a35c;">%rep </span><span style="color:#0086b3;">25
</span><span style="font-weight:bold;color:#795da3;">hollow_line
</span><span style="color:#62a35c;">%endrep
</span><span style="font-weight:bold;color:#795da3;">full_line
</span></code></pre>
<p><a href="http://www.tortall.net/projects/yasm/manual/html/nasm-multi-line-macros.html">Macros can have parameters</a>, and the 0s above is us declaring that there will be zero parameters.</p>
<p>Your code should now be similar to <a href="/asmtut/lesson03/part3.asm">part3.asm</a> which outputs a lovely playful area just begging for a game of snake!</p>
<p><em>Exercise:</em> Make something nice with macros. I'm pretty sure there must be an even less ugly way of defining this data :) It should be possible to make a macro that takes in width and height and spits out a box.</p>
<p><em>UPDATE:</em> Solution by Knut at <a href="http://www.pastebin.com/XszHNdcx">http://www.pastebin.com/XszHNdcx</a></p>
<p><em>Next lesson:</em> <a href="https://magnushoff.com/blog/asmtut-4/">Asmtut 4: Input</a> (Will contain proper assembly! I promise!)</p>
Asmtut 2: Hello world!https://magnushoff.com/blog/asmtut-2/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p><em>Aside: For your convenience, please use a <code>Makefile</code>. To assemble and link, simply execute <code>make</code>.</em></p>
<pre style="background-color:#ffffff;">
<code><span style="font-style:italic;color:#969896;"># Please be aware that the indentation must be a tab character (no spaces)
</span><span style="font-weight:bold;color:#795da3;">
all</span><span style="font-weight:bold;color:#a71d5d;">: </span><span style="color:#183691;">hello true
</span><span style="font-weight:bold;color:#323232;">%</span><span style="font-weight:bold;color:#a71d5d;">: </span><span style="color:#323232;">%</span><span style="color:#183691;">.o
</span><span style="color:#323232;">ld -macosx_version_min 10.6 -o $@ -e main $<
</span><span style="font-weight:bold;color:#323232;">%</span><span style="font-weight:bold;color:#795da3;">.o</span><span style="font-weight:bold;color:#a71d5d;">: </span><span style="color:#323232;">%</span><span style="color:#183691;">.asm
</span><span style="color:#323232;">nasm -f macho64 -o $@ $<
</span></code></pre><h2 id="step-3-calling-write">Step 3: Calling write</h2>
<p>We are now ready for "Hello world"! We know how to do a syscall, so let's see (in <code>/usr/include/sys/syscall.h</code>) if we can use one for printing. <code>"SYS_write" (4)</code> looks promising, and in fact it is the POSIX write that has a documented C API we can read with "man 2 write":</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#0086b3;">ssize_t </span><span style="font-weight:bold;color:#795da3;">write</span><span style="color:#323232;">(</span><span style="font-weight:bold;color:#a71d5d;">int </span><span style="color:#323232;">fildes, </span><span style="font-weight:bold;color:#a71d5d;">const void *</span><span style="color:#323232;">buf, </span><span style="color:#0086b3;">size_t </span><span style="color:#323232;">nbyte);
</span></code></pre>
<p>Oh, wow. There's lots of arguments and datatypes and stuff. Again, the ABI tells us to put arguments in sequence in the registers <code>rdi</code>, <code>rsi</code>, <code>rdx</code>, <code>rcx</code>, <code>r8</code> and <code>r9</code>. And if we need more arguments, we will have to look at the ABI again.</p>
<p><em>filedes:</em> We want to write to standard out, and its fileno is defined in POSIX as 1. We can verify this information against <code>/usr/include/unistd.h</code>, where <code>STDOUT_FILENO</code> is defined as 1.</p>
<p><em>buf:</em> We need a string to write. Let's postpone that slightly!</p>
<p><em>nbyte:</em> This is the count of bytes to write. Let's write 0 of them for now.</p>
<p>This should translate to the following assembly:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000004</span><span style="font-style:italic;color:#969896;"> ; Again, 0x02000000 added for a "Unix" type call
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; filedes = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="color:#0086b3;">0x1337c0d3</span><span style="font-style:italic;color:#969896;"> ; Filler value, we postponed the problem of buf
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Write zero bytes from this buffer
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>Combining with the skeleton "true.asm" you should be able to come up with an "hello.asm" that looks something like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000004</span><span style="font-style:italic;color:#969896;"> ; SYS_write
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; filedes = STDOUT_FILENO = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="color:#0086b3;">0x1337c0d3</span><span style="font-style:italic;color:#969896;"> ; Filler value, we postponed the problem of buf
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Write zero bytes from this buffer
</span><span style="font-weight:bold;color:#a71d5d;">syscall
mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001</span><span style="font-style:italic;color:#969896;"> ; SYS_exit
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Process exit value
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>When assembled and linked, this should reliably execute without writing anything or failing. Even though we give a bogus pointer value in, it never gets dereferenced since the <em>nbyte</em> argument is 0.</p>
<p>Now we are calling <code>write</code>! Let's give it something to say.</p>
<h2 id="step-4-data-section">Step 4: Data section</h2>
<p>We need a string constant to write to stdout, and we are going to put one in static storage. We do this by putting in stuff for the assembler and linker that isn't assembly code. First, we partition the file into two sections, one for data and one for code. The one for data is called <code>.data</code> and the one for code is called <code>.text</code>.</p>
<p>Put a line with <code>section .data</code> at the top of your file, put in a couple of blank lines and then <code>section .text</code> as a header for your code. Everything that's under the <code>section .data</code> header, but above the <code>section .text</code> header will now be said to be in the <em>data</em> section, while everything underneath the <code>section .text</code> header will be said to be in the text section. It should now look like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">.</span><span style="color:#62a35c;">section </span><span style="font-weight:bold;color:#795da3;">.data
.</span><span style="color:#62a35c;">section </span><span style="font-weight:bold;color:#795da3;">.text
</span><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000004</span><span style="font-style:italic;color:#969896;"> ; SYS_write
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; filedes = STDOUT_FILENO = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="color:#0086b3;">0x1337c0d3</span><span style="font-style:italic;color:#969896;"> ; Filler value, we postponed the problem of buf
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Write zero bytes from this buffer
</span><span style="font-weight:bold;color:#a71d5d;">syscall
mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001</span><span style="font-style:italic;color:#969896;"> ; SYS_exit
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Process exit value
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>In the data section, put a definition for a string. It is going to be <em>data</em> that we specify <em>bytewise</em>, so we are going to use the keyword db:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#795da3;">hello_world </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"Hello world!"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span></code></pre>
<p><code>0x0a</code> is the control character for a newline. In C we would have used <code>"\n"</code>, which can be mapped to other values depending on the platform. C is much more cross platform compatible than assembly!</p>
<p>Now, we can update the call to <code>write</code> with proper values:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">hello_world</span><span style="font-style:italic;color:#969896;"> ; The assembler and/or linker will make sure rsi gets the address of the string above
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">13</span><span style="font-style:italic;color:#969896;"> ; The string, including the newline, is 13 bytes long
</span></code></pre>
<p>The file should now look like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">section </span><span style="font-weight:bold;color:#795da3;">.data
hello_world </span><span style="color:#62a35c;">db </span><span style="color:#183691;">"Hello World!"</span><span style="color:#323232;">, </span><span style="color:#0086b3;">0x0a
</span><span style="font-weight:bold;color:#795da3;">section .text
</span><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000004</span><span style="font-style:italic;color:#969896;"> ; SYS_write
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">1</span><span style="font-style:italic;color:#969896;"> ; filedes = STDOUT_FILENO = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rsi, </span><span style="font-weight:bold;color:#795da3;">hello_world</span><span style="font-style:italic;color:#969896;"> ; The address of hello_world string
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdx, </span><span style="color:#0086b3;">13</span><span style="font-style:italic;color:#969896;"> ; The size to write
</span><span style="font-weight:bold;color:#a71d5d;">syscall
mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001</span><span style="font-style:italic;color:#969896;"> ; SYS_exit
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Exit status
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>Congratulations! You have implemented "Hello world" in assembly! :)</p>
<h2 id="step-5-disassembly">Step 5: Disassembly</h2>
<p>A big point of knowing assembly is to know exactly what is happening under the hood. However, the <code>mov rsi, hello_world</code> instruction above gets lots of interpretation. Let's look closer with otool.</p>
<p>First, look at the data section:</p>
<pre style="background-color:#ffffff;">
<code>otool -d ./hello
</code></pre>
<p>The output I get is:</p>
<pre style="background-color:#ffffff;">
<code>./hello:
(__DATA,__data) section
0000000000002000 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 0a
</code></pre>
<p><code>0000000000002000</code> is the address where the data section starts, and the bytes from <code>48</code> through <code>0a</code> is the string we put in, ASCII coded and presented in hex. Nice. This is pretty much exactly what we put in. Notice that there is no label, no information on structure inside the data section.</p>
<p>Next, look at the text/code section:</p>
<pre style="background-color:#ffffff;">
<code>otool -t ./hello
./hello:
(__TEXT,__text) section
0000000000001fd9 b8 04 00 00 02 bf 01 00 00 00 48 be 00 20 00 00
0000000000001fe9 00 00 00 00 ba 0d 00 00 00 0f 05 b8 01 00 00 02
0000000000001ff9 bf 00 00 00 00 0f 05
</code></pre>
<p>Oh. That's not very readable. We can get <em>otool</em> to disassemble it for us, but beware, for it uses the ugly AT&T syntax:</p>
<pre style="background-color:#ffffff;">
<code>otool -tv ./hello
./hello:
(__TEXT,__text) section
main:
0000000000001fd9 movl $0x02000004,%eax
0000000000001fde movl $0x00000001,%edi
0000000000001fe3 movq $0x0000000000002000,%rsi
0000000000001fed movl $0x0000000d,%edx
0000000000001ff2 syscall
0000000000001ff4 movl $0x02000001,%eax
0000000000001ff9 movl $0x00000000,%edi
0000000000001ffe syscall
</code></pre>
<p>The order of the operands to mov are backwards! Sigh. The big number on the left is just the address of each instruction.</p>
<p>This looks pretty much like what we put it. The most interesting difference is the line where we set rsi. In our source code it is <code>mov rsi, hello_world</code>, but the result, translated to Intel syntax, is <code>mov rsi, 0x0000000000002000</code>. This is the address in the data section where our string starts, so this is pretty good news :) This gives an insight into how symbols are dereferenced during assembling and linking.</p>
<p>The second thing we notice is that some of our "r"-s have been turned into "e"-s. This is an optimization nasm put in for us. The registers, as we know, have not always been 64 bits wide, so there is some legacy here. Let's consider <code>rax</code> as an example:</p>
<p>Using <code>rax</code>, you speak of the entire 64 bit register. eax is the low 32 bits. The low 16 bits are called ax and the low 8 bits <code>al</code>. Additionally, bits 8-15 can be accessed directly as <code>ah</code>.</p>
<p>When defining the x86-64 instruction set, AMD realized that requiring you to put 64 bits of data into the text section every time you wanted to set a register would quickly fill up code with lots of worthless 0 bytes, in turn filling up expensive cache lines in the processor with the same. So as a special rule, when setting a 32 bit register, the high 32 bits always get nulled out. So <code>mov eax, 0x12345678</code> has the same effect as <code>mov rax,0x0000000012345678</code>, but the <code>eax</code> one is four bytes smaller than the rax one.</p>
<p>Now we can understand the disassembly from <em>otool</em>, and how it relates to the original source code. Hooray!</p>
<p><em>Achievement awarded:</em> Followed a programming tutorial where "Hello world" was the <strong>second</strong> lesson.</p>
<p><em>Next lesson:</em> <a href="https://magnushoff.com/blog/asmtut-3/">Asmtut 3: Redundancies</a></p>
Asmtut 1: truehttps://magnushoff.com/blog/asmtut-1/Sat, 07 Sep 2019 00:00:00 +0000<p><em>This was originally posted on Google+, which has now been shut down. It was
helpfully converted to Markdown by Robert Jacobson after which I adjusted it
for reposting here. I have dated it at its original posting date, but it was
posted here on 2019-09-07.</em></p>
<hr />
<p>In an effort to educate the dunces Knut and Jon who apparently <em>never programmed in assembly</em>, I am going to post a step by step instruction on making a snake game in x86-64/amd64 assembly on a modern operating system. Here. As if this were some kind of a blog. I am learning x86-64 assembly as I go, which makes this even more of a blog-like experience.</p>
<p>The modern operating system I chose is OS X 10.6. The same assembly might work directly in BSD and should work with only minor changes in Linux. The build rules will require additional changes :)</p>
<p>The goal is to make an interactive realtime snake game in the terminal. Graphics was a lot easier in the old days when you would just write directly to the video memory, but that is unavailable now and I would rather make this more contemporary than making it more graphical but limit it to Dosbox.</p>
<p>For programming assembly we need an assembler. GCC has got one, but it uses AT&T syntax for no good reason, which is harder to read and write than Intel syntax. Therefore, we choose <a href="http://www.nasm.us/pub/nasm/releasebuilds/2.10.05/macosx/nasm-2.10.05-macosx.zip">NASM</a>, which implements Intel syntax. Install the nasm binary somewhere in your <code>PATH</code>. You might already have a <code>/usr/bin/nasm</code> from Xcode which is too old to support x86-64, so be sure not to confuse that one with the one you just installed.</p>
<h2 id="step-1-assemble-link-and-execute">Step 1: Assemble, link and execute</h2>
<p>In this installment, we are implementing the <code>true</code> command line utility. It executes and returns 0, indicating success. An equivalent C program is <code>int main() { return 0; }</code>. Simple, but a good place to start to check that all the tools are working and that everything is in place.</p>
<p>In reality, unlike the overly abstract world of C, the entry to a process does not act exactly as a function call, and the exit is not like a function return. C programs usually get compiled with trampoline functions to make all of this more convenient. We will go straight for calling the <code>exit</code> function with 0 as the argument.</p>
<p>Although, before we get to that, we need to supply an entry point to our program and export this symbol to the linker. We do the exporting first, by declaring <code>global main</code> at the top of our new file <code>true.asm</code>. Go ahead, it is safe. Now, immediately below it we write <code>main:</code> on a line for itself. <code>main:</code> is a label, and referring to this label anywhere will give us its address. This is what the linker needs. <code>true.asm</code> should now look like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span></code></pre>
<p>This is actually all we need to assemble and link.</p>
<p><strong>Assembling (.asm -> .o):</strong> <code>nasm -f macho64 -o true.o true.asm</code></p>
<p><code>-f macho64</code> tells NASM to produce an object file of the Mach-O 64bit format. This should be <code>elf64</code> for Linux, for example. <code>nasm -hf</code> gives you a list of the formats NASM supports.</p>
<p><strong>Linking (.o -> executable):</strong> <code>ld -macosx_version_min 10.6 -o true -e main true.o</code></p>
<p><code>-e main</code> tells the linker that <code>main</code> is the label of our entry point, and the linker can find it because we have <code>global main</code> in our <code>.asm</code> file.</p>
<p>It should now be possible to execute <code>./true</code>, and it will probably cause the nondescript error message "Bus error: 10" to appear.</p>
<h2 id="step-2-exit-0">Step 2: exit(0)</h2>
<p>We are not going to call the <code>exit</code> function in the C runtime library, but rather the <code>exit</code> system call via the OS's syscall functionality. There is some information on this in <code>/usr/include/sys/syscall.h</code>, and in it we can see that <code>SYS_exit</code> has identification number 1. Nice.</p>
<p>Thanks to <a href="http://thexploit.com/secdev/mac-os-x-64-bit-assembly-system-calls/">http://thexploit.com/secdev/mac-os-x-64-bit-assembly-system-calls/</a> I also found out that since <code>exit</code> is classified as a Unix call, it gets to have an identifier of [whatever's in syscall.h] + <code>0x02000000</code>. That is, <code>0x02000001</code>. Great. We now know how to identify the system call <code>exit</code>.</p>
<p>We also know that <code>exit</code> takes an argument, the value to return.</p>
<p>According to the <a href="http://www.x86-64.org/documentation/abi.pdf">ABI</a> we should put the syscall number in the register <code>rax</code> and the first argument in the register <code>rdi</code>. Think of registers as (global-ish) variables that you don't get to name. So, in pseudocode, we want something like:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#323232;">rax := 0x02000001; // Put the ID for SYS_exit into rax
rdi := 0; // Put the desired exit status value into rdi
performTheSyscall;
</span></code></pre>
<p>This is quite easy to express in assembly:</p>
<pre style="background-color:#ffffff;">
<code><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0
</span><span style="font-weight:bold;color:#a71d5d;">syscall
</span></code></pre>
<p>Now, <code>test.asm</code> should look something like this:</p>
<pre style="background-color:#ffffff;">
<code><span style="color:#62a35c;">global </span><span style="font-weight:bold;color:#795da3;">main
main:
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rax, </span><span style="color:#0086b3;">0x02000001</span><span style="font-style:italic;color:#969896;"> ; System call number for exit = 1
</span><span style="font-weight:bold;color:#a71d5d;">mov </span><span style="color:#323232;">rdi, </span><span style="color:#0086b3;">0</span><span style="font-style:italic;color:#969896;"> ; Exit success = 0
</span><span style="font-weight:bold;color:#a71d5d;">syscall</span><span style="font-style:italic;color:#969896;"> ; Invoke the kernel
</span></code></pre>
<p><code>syscall</code> is actually a dedicated assembly instruction that was introduced in the x86-64 instruction set to make calls to the operating system more snappy.</p>
<p>Now you should be able to assemble, link and run this proper implementation and it should act exactly like the <code>true</code> built-in in bash.</p>
<p>Exercise for the reader: Modify this to implement <code>false</code> ;)</p>
<p>OBTW TIL: Google+ doesn't offer formatting for code.</p>
<p><em>Next lesson:</em> <a href="https://magnushoff.com/blog/asmtut-2/">Asmtut 2: Hello world!</a></p>