472 lines
No EOL
30 KiB
HTML
472 lines
No EOL
30 KiB
HTML
<!DOCTYPE html>
|
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>Ludum Dare 34 Postmortem / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>Ludum Dare 34 Postmortem</a></h1><p class='date'>Posted on December 15th, 2015.</p><p>This past weekend was <a href="http://ludumdare.com/compo/">Ludum Dare 34</a>. Ludum Dare is a thrice-a-year event
|
|
where a theme is chosen and people have 48 hours (for the competition) or 72
|
|
hours (for the "casual" jam) to create a game based on a theme.</p>
|
|
|
|
<p>I actually managed to have some free time for a change, so I decided to give it
|
|
a go. I've got a lot of experience programming and a little bit in "sprinting"
|
|
like this (my team <a href="http://djangodash.com/judging/c2/results/team/49/">placed second in Django Dash</a> a few years ago),
|
|
but I haven't made very many games. Still, by the end of the jam I managed to
|
|
make something that's not quite a game but <em>is</em> pretty fun to play around with.
|
|
I figured I'd write about the process while it's all still fresh in my mind.</p>
|
|
|
|
<ol class="table-of-contents"><li><a href="index.html#s1-my-game">My Game</a><ol><li><a href="index.html#s2-language-code">Language & Code</a></li><li><a href="index.html#s3-play">Play</a></li><li><a href="index.html#s4-gameplay">Gameplay</a></li><li><a href="index.html#s5-controls">Controls</a></li></ol></li><li><a href="index.html#s6-good-choices">Good Choices</a><ol><li><a href="index.html#s7-familiar-environment">Familiar Environment</a></li><li><a href="index.html#s8-streaming-and-talking">Streaming and Talking</a></li><li><a href="index.html#s9-releasing-before-the-end">Releasing Before the End</a></li><li><a href="index.html#s10-getting-feedback">Getting Feedback</a></li><li><a href="index.html#s11-profiling-and-performance">Profiling and Performance</a></li><li><a href="index.html#s12-ruthless-simplicity">Ruthless Simplicity</a></li><li><a href="index.html#s13-sleeping">Sleeping</a></li></ol></li><li><a href="index.html#s14-bad-choices">Bad Choices</a><ol><li><a href="index.html#s15-clinging-to-failed-mechanics">Clinging to Failed Mechanics</a></li><li><a href="index.html#s16-last-minute-additions">Last-Minute Additions</a></li><li><a href="index.html#s17-working-alone">Working Alone</a></li></ol></li><li><a href="index.html#s18-clojure">Clojure</a><ol><li><a href="index.html#s19-destructuring-is-nice">Destructuring is Nice</a></li><li><a href="index.html#s20-fewer-superfluous-parentheses">Fewer Superfluous Parentheses</a></li><li><a href="index.html#s21-the-tooling-is-good">The Tooling is Good</a></li><li><a href="index.html#s22-but-the-compiler-is-not-helpful">But the Compiler Is Not Helpful</a></li><li><a href="index.html#s23-errors-are-still-terrible">Errors are Still Terrible</a></li><li><a href="index.html#s24-old-bugs">Old Bugs</a></li><li><a href="index.html#s25-rip-your-heap">RIP Your Heap</a></li></ol></li><li><a href="index.html#s26-conclusion">Conclusion</a></li></ol>
|
|
|
|
<h2 id="s1-my-game"><a href="index.html#s1-my-game">My Game</a></h2>
|
|
|
|
<p>The theme this year was actually <em>tied</em> in voting, so there were two:</p>
|
|
|
|
<ul>
|
|
<li>Growing</li>
|
|
<li>Two Button Controls</li>
|
|
</ul>
|
|
|
|
<h3 id="s2-language-code"><a href="index.html#s2-language-code">Language & Code</a></h3>
|
|
|
|
<p>I knew going in that I wanted to make something with ASCII graphics with Clojure
|
|
and <a href="https://sjl.bitbucket.io/clojure-lanterna">clojure-lanterna</a>. Once I thought about the themes I decided to make
|
|
a simulation of a world with plants and animals. I settled on the name "Silt"
|
|
(the stuff at the bottom of riverbeds that slowly accumulates and reshapes the
|
|
world) and got to work.</p>
|
|
|
|
<p>You can get the source for the game <a href="https://bitbucket.org/sjl/silt">on BitBucket</a>. There's also
|
|
a mirror on GitHub if you prefer git.</p>
|
|
|
|
<h3 id="s3-play"><a href="index.html#s3-play">Play</a></h3>
|
|
|
|
<p>There's a prebuilt jar file <a href="https://bitbucket.org/sjl/silt">on BitBucket</a> if you want to play the
|
|
game on your computer.</p>
|
|
|
|
<p>If you don't want to run some random code on your machine, I understand. I've
|
|
spun up a server you can telnet into to play:</p>
|
|
|
|
<pre><code>telnet silt.stevelosh.com
|
|
</code></pre>
|
|
|
|
<p>For best results use a terminal with a dark background. If there are a bunch of
|
|
people playing the server may be slow, or fail entirely with out-of-memory
|
|
errors. Sorry, it's just an 8gb Linode I'm funding out of my own pocket.</p>
|
|
|
|
<h3 id="s4-gameplay"><a href="index.html#s4-gameplay">Gameplay</a></h3>
|
|
|
|
<p>You are the god of a <a href="https://en.wikipedia.org/wiki/Torus">toroidal</a> world. Initially it's inhabited by a small
|
|
population of four hundred and one creatures. Time ticks by at a few ticks per
|
|
second.</p>
|
|
|
|
<p><a href="../../../../static/images/blog/2015/12/silt-initial.gif"><img src="../../../../static/images/blog/2015/12/silt-initial.gif" alt="Silt Initial World"></a></p>
|
|
|
|
<p>The creatures need energy to survive. They can get energy by eating fruit from
|
|
shrubs or being near water.</p>
|
|
|
|
<p>If a creature has enough energy it might reproduce by splitting off a clone of
|
|
itself. The clone will be identical to its parent (sibling?), though there is
|
|
a slight chance of mutation.</p>
|
|
|
|
<p>The ideal body temperature for a creature is 0 degrees. The world starts at
|
|
that temperature, creating a paradise. As the god of the world, the only way
|
|
you can interact with it is to increase or decrease the temperature.</p>
|
|
|
|
<p>If the temperature of the world is different than the temperature of a creature,
|
|
they will need to spend more energy to maintain their body heat. This makes it
|
|
more difficult to survive. Swinging the temperature in large increments quickly
|
|
is a sure way to kill off the population.</p>
|
|
|
|
<p>The creatures are not entirely at the mercy of the climate, however. Creatures
|
|
have an "insulation" score that represents fur or skin. More insulation
|
|
protects the creature from the outside climate, at the expense of a small amount
|
|
of energy.</p>
|
|
|
|
<p>The mechanics of temperature/insulation and reproduction/mutation interact to
|
|
cause evolution. If you slowly increase the temperature of the world over a few
|
|
thousand ticks, children with more insulation are more likely to survive, and so
|
|
your creatures will tend to evolve more insulation over time.</p>
|
|
|
|
<p>Creatures' movement patterns are also affected by mutation. Over time your
|
|
creatures will likely evolve to wander instead of staying stationary, because
|
|
fruit takes time to grow and so it's more effective to keep moving and
|
|
gathering.</p>
|
|
|
|
<p>Creatures can also change their colors and glyphs through mutation, but this is
|
|
much rarer. After letting the game run for a while you'll probably notice
|
|
"gangs" of creatures with similar characteristics, all descended from a single
|
|
parent.</p>
|
|
|
|
<p><a href="../../../../static/images/blog/2015/12/silt-later.gif"><img src="../../../../static/images/blog/2015/12/silt-later.gif" alt="Silt Later World"></a></p>
|
|
|
|
<p>Finally, there are eight mysterious objects scattered throughout the landscape.
|
|
Each one does something, but discovering exactly <em>what</em> will be difficult
|
|
(unless you read the source).</p>
|
|
|
|
<h3 id="s5-controls"><a href="index.html#s5-controls">Controls</a></h3>
|
|
|
|
<p>The controls are pretty basic:</p>
|
|
|
|
<ul>
|
|
<li><strong><code>arrow keys</code></strong> to move your view of the world.</li>
|
|
<li><strong><code>R</code></strong> to reset the world.</li>
|
|
<li><strong><code>escape</code></strong> to quit the game.</li>
|
|
</ul>
|
|
|
|
<p>Put your cursor over a creature to see their stats:</p>
|
|
|
|
<ul>
|
|
<li><strong><code>hjkl</code></strong> or <strong><code>wasd</code></strong> to move your cursor.</li>
|
|
</ul>
|
|
|
|
<p>The world ticks along, but you can freeze time:</p>
|
|
|
|
<ul>
|
|
<li><strong><code>space</code></strong> to pause/unpause time.</li>
|
|
</ul>
|
|
|
|
<p>Those are the basic controls. To actually <em>interact</em> with the world you have
|
|
only two options (in accordance with the "Two Button Controls" theme):</p>
|
|
|
|
<ul>
|
|
<li><strong><code>+</code></strong> to make the world one degree hotter.</li>
|
|
<li><strong><code>-</code></strong> to make the world one degree colder.</li>
|
|
</ul>
|
|
|
|
<h2 id="s6-good-choices"><a href="index.html#s6-good-choices">Good Choices</a></h2>
|
|
|
|
<p>I managed to make a working, fairly interesting game within the time limit, so
|
|
I'm pretty happy with most of my choices.</p>
|
|
|
|
<h3 id="s7-familiar-environment"><a href="index.html#s7-familiar-environment">Familiar Environment</a></h3>
|
|
|
|
<p>I decided to make Silt in Clojure. If you follow me on <a href="http://twitch.tv/stevelosh">Twitch</a> you might
|
|
have noticed I've been getting more into Common Lisp these days. I don't really
|
|
like writing in Clojure any more, but I used it for the jam because I wanted an
|
|
environment I was familiar with.</p>
|
|
|
|
<p>This was a good decision. I wouldn't have finished if I tried to use Common
|
|
Lisp — I'd probably still be struggling with whatever its Curses library is.</p>
|
|
|
|
<p>A 72-hour jam is not the time to be trying out a new language or framework
|
|
you've never used before if you want to have a finished product at the end.
|
|
Pick something you know well so you don't spend 30% of your time trying to
|
|
Google things.</p>
|
|
|
|
<h3 id="s8-streaming-and-talking"><a href="index.html#s8-streaming-and-talking">Streaming and Talking</a></h3>
|
|
|
|
<p>I streamed a bit of the coding process a couple times over the weekend (look in
|
|
past broadcasts and they might still be there). I didn't have a ton of viewers,
|
|
but it was good to talk things out. Sometimes the stream was my <a href="https://en.wikipedia.org/wiki/Rubber_duck_debugging">rubber duck</a>
|
|
and it helped a bunch.</p>
|
|
|
|
<h3 id="s9-releasing-before-the-end"><a href="index.html#s9-releasing-before-the-end">Releasing Before the End</a></h3>
|
|
|
|
<p>Ludum Dare ends at a certain time, but there's a "release hour" afterword when
|
|
you can take an hour to package up the final version of your game. I think it's
|
|
better if you don't leave it until then to run through your packaging/release
|
|
process at least once, though. Once I had the game in a runnable (if not yet
|
|
very fun) state, I made it into a jar to make sure that everything works.</p>
|
|
|
|
<p>You don't want to find out your build
|
|
process is fucked when you only have 40 minutes left, so do a dry run early and
|
|
discover any snags while you have lots of time to fix them.</p>
|
|
|
|
<h3 id="s10-getting-feedback"><a href="index.html#s10-getting-feedback">Getting Feedback</a></h3>
|
|
|
|
<p>Once I had a playable game I asked my friend <a href="http://havethis.info/">Hafdís</a> to see if it would run
|
|
on Windows. Luckily it did — Java and Lanterna are pretty nicely
|
|
cross-platform.</p>
|
|
|
|
<p>She ran the game in the background for a while and showed me the results later.
|
|
It's really helpful to get a second opinion about your game (or website or
|
|
whatever you're making) as you're working on it. It's too easy to get too close
|
|
to your own game and not realize what's fun or painful about it any more.</p>
|
|
|
|
<p>Asking in the IRC channel can work, but generally everyone there is busy working
|
|
on their own games so you won't get a ton of takers. You can ask someone
|
|
online, but I think in-person is better if at all possible. Seeing someone else
|
|
enjoy something you've made is a big morale boost, which can be important when
|
|
you're dealing with tricky bugs later.</p>
|
|
|
|
<h3 id="s11-profiling-and-performance"><a href="index.html#s11-profiling-and-performance">Profiling and Performance</a></h3>
|
|
|
|
<p>During the last day I spent an hour or so using a profiler to identify the
|
|
hottest spots in the code and improving performance. I didn't focus too much on
|
|
speed, but a little bit of attention can go a long way.</p>
|
|
|
|
<h3 id="s12-ruthless-simplicity"><a href="index.html#s12-ruthless-simplicity">Ruthless Simplicity</a></h3>
|
|
|
|
<p>One of the main reasons I managed to finish despite my lack of experience making
|
|
games was being ruthless about making the game as simple as possible. I have
|
|
a ton of ideas that I could potentially do, but cramming them all into the game
|
|
would take far too long and would probably create a giant mess.</p>
|
|
|
|
<p>Focusing on one core mechanic (temperature) and building the things that
|
|
interact with it got me pretty far. The fact that the theme was "Two Button
|
|
Controls" really helped me a lot with this, because it forced me to come up with
|
|
a mechanic simple enough to encode in two buttons.</p>
|
|
|
|
<h3 id="s13-sleeping"><a href="index.html#s13-sleeping">Sleeping</a></h3>
|
|
|
|
<p>I was hanging out in the official IRC channel over the weekend and noticed
|
|
a bunch of people talking about how tired they were and how little sleep they
|
|
were getting.</p>
|
|
|
|
<p>Because the time frame is so short there's a tendency to want to stay awake for
|
|
as much time as possible to use as many hours as you can. I think this is
|
|
usually a mistake. During this weekend, and also for the Django Dash weekend,
|
|
I stuck to my normal sleeping/waking schedule and I think it was the right
|
|
choice. You may get <em>more</em> hours of work in if you don't sleep, but you get
|
|
<em>better</em> hours of work if you're rested, fed, and showered.</p>
|
|
|
|
<p>This is especially tricky if your timezone is offset with the event. The themes
|
|
were announced at 2 AM my time, and while I would have loved to be awake and
|
|
dive in right away I chose to go to sleep instead and just get started the next
|
|
day. A screwed up sleep schedule is almost as bad as not enough sleep (for me
|
|
at least).</p>
|
|
|
|
<h2 id="s14-bad-choices"><a href="index.html#s14-bad-choices">Bad Choices</a></h2>
|
|
|
|
<p>I made a few mistakes along the way too (luckily none of them prevented me from
|
|
finishing).</p>
|
|
|
|
<h3 id="s15-clinging-to-failed-mechanics"><a href="index.html#s15-clinging-to-failed-mechanics">Clinging to Failed Mechanics</a></h3>
|
|
|
|
<p>One of the first mechanics I added to the game was aging — creatures would age
|
|
over time and eventually die of old age. Immediately I had trouble balancing
|
|
this against reproduction to avoid population explosions and extinctions, but
|
|
I stuck with it for quite a while trying to make it work.</p>
|
|
|
|
<p>Eventually I tore the entire aging mechanic out and replaced it with hunger,
|
|
which ended up working far, far better. In hindsight I should have abandoned
|
|
aging much sooner, as soon as it was obvious that it was making things painful
|
|
and unfun.</p>
|
|
|
|
<h3 id="s16-last-minute-additions"><a href="index.html#s16-last-minute-additions">Last-Minute Additions</a></h3>
|
|
|
|
<p>I added a couple of mysterious objects right before I packaged up the final
|
|
version of the game and I didn't have time to playtest them very much. I tested
|
|
that they didn't crash the game, but I assumed their effects would be minor
|
|
curiosities and didn't worry too much.</p>
|
|
|
|
<p>Now that I've tested it a bit more, they have a bigger effect than I thought
|
|
and drastically affect the world. I should have either tested them more or held
|
|
off on them until I had time to test.</p>
|
|
|
|
<h3 id="s17-working-alone"><a href="index.html#s17-working-alone">Working Alone</a></h3>
|
|
|
|
<p>This one was unavoidable for me this time, but I'm sure Silt could have been
|
|
a lot more interesting if I had worked with another programmer, an artist, or
|
|
a sound designer.</p>
|
|
|
|
<h2 id="s18-clojure"><a href="index.html#s18-clojure">Clojure</a></h2>
|
|
|
|
<p>I'll end with a few notes on the experience of writing Silt in Clojure. Like
|
|
I said earlier: I don't particularly like Clojure any more, but it was familiar
|
|
so I ran with it. After doing a bunch of Common Lisp lately a few things jumped
|
|
out at me about writing in Clojure, so I wanted to get them down on paper before
|
|
I forget.</p>
|
|
|
|
<h3 id="s19-destructuring-is-nice"><a href="index.html#s19-destructuring-is-nice">Destructuring is Nice</a></h3>
|
|
|
|
<p>Clojure's ubiquitous destructuring is really nice. Common Lisp has
|
|
<code>destructuring-bind</code> but it's not as powerful as it is in Clojure, and it's
|
|
certainly not threaded through the language nearly as much.</p>
|
|
|
|
<p>Destructuring in function arguments is <em>really</em> handy — I almost want to write
|
|
a <code>defun-destructured</code> macro that will let me do it in CL.</p>
|
|
|
|
<h3 id="s20-fewer-superfluous-parentheses"><a href="index.html#s20-fewer-superfluous-parentheses">Fewer Superfluous Parentheses</a></h3>
|
|
|
|
<p>In Clojure's syntax there seems to be a general theme of using fewer
|
|
parentheses. I don't just mean using brackets and curly braces for other
|
|
literals either. Take the humble <code>let</code> in Clojure:</p>
|
|
|
|
<pre><code>(let [x (foo)
|
|
y (bar)]
|
|
(+ x y))</code></pre>
|
|
|
|
<p>If you translate the vector literal to a Common Lisp list, it would look like
|
|
this:</p>
|
|
|
|
<pre><code>(let (x (foo)
|
|
y (bar))
|
|
(+ x y))</code></pre>
|
|
|
|
<p>But Common Lisp actually makes you surround the binding pairs with <em>another</em> set
|
|
of parentheses:</p>
|
|
|
|
<pre><code>(let ((x (foo))
|
|
(y (bar)))
|
|
(+ x y))</code></pre>
|
|
|
|
<p>This is annoying. There are always going to be pairs of binding forms, why do
|
|
I need to wrap them in extra parens? Clojure's way is cleaner.</p>
|
|
|
|
<h3 id="s21-the-tooling-is-good"><a href="index.html#s21-the-tooling-is-good">The Tooling is Good</a></h3>
|
|
|
|
<p>Jvisualvm's statistical profiler is a bit homely, but it gets the job done.
|
|
It's nice to have mature tools to introspect what your code is doing at runtime.</p>
|
|
|
|
<p><code>lein uberjar</code> just worked and produced a jar that runs fine on Windows and OS
|
|
X. This was a great relief. I haven't looked at packaging up a Common Lisp app
|
|
but I am not optimistic.</p>
|
|
|
|
<h3 id="s22-but-the-compiler-is-not-helpful"><a href="index.html#s22-but-the-compiler-is-not-helpful">But the Compiler Is Not Helpful</a></h3>
|
|
|
|
<p>SBCL warns me when I'm doing something stupid:</p>
|
|
|
|
<pre><code><span class="code">CL-USER> <span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> square <span class="paren2">(<span class="code">x</span>)</span> <span class="paren2">(<span class="code">* x x</span>)</span></span>)</span>
|
|
|
|
SQUARE
|
|
CL-USER> <span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> bad <span class="paren2">(<span class="code">x y</span>)</span>
|
|
<span class="paren2">(<span class="code">+ x <span class="paren3">(<span class="code">square y y</span>)</span></span>)</span></span>)</span>
|
|
<span class="comment">; in: DEFUN BAD
|
|
</span><span class="comment">; (SQUARE Y Y)
|
|
</span><span class="comment">;
|
|
</span><span class="comment">; caught STYLE-WARNING:
|
|
</span><span class="comment">; The function was called with two arguments, but wants exactly one.
|
|
</span><span class="comment">;
|
|
</span><span class="comment">; compilation unit finished
|
|
</span><span class="comment">; caught 1 STYLE-WARNING condition
|
|
</span>
|
|
BAD
|
|
CL-USER></span></code></pre>
|
|
|
|
<p>Clojure happily lets me be stupid without a peep:</p>
|
|
|
|
<pre><code><span class="code">user=> <span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> square <span class="paren2">[<span class="code">x</span>]</span> <span class="paren2">(<span class="code">* x x</span>)</span></span>)</span>
|
|
#'user/square
|
|
user=> <span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> bad <span class="paren2">[<span class="code">x y</span>]</span>
|
|
#_=> <span class="paren2">(<span class="code">+ x <span class="paren3">(<span class="code">square y y</span>)</span></span>)</span></span>)</span>
|
|
#'user/bad
|
|
user=></span></code></pre>
|
|
|
|
<p>I'm often stupid, so I prefer a compiler that helps prevent it.</p>
|
|
|
|
<h3 id="s23-errors-are-still-terrible"><a href="index.html#s23-errors-are-still-terrible">Errors are Still Terrible</a></h3>
|
|
|
|
<p>Clojure stack traces have historically been awful. When I ran into my first
|
|
error this time I noticed that the stack trace was... nonexistent?</p>
|
|
|
|
<pre><code><span class="code">user=> <span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> foo <span class="paren2">[<span class="code"></span>]</span> <span class="paren2">(<span class="code">/ 1 0</span>)</span></span>)</span>
|
|
#'user/foo
|
|
user=> <span class="paren1">(<span class="code">foo</span>)</span>
|
|
|
|
ArithmeticException Divide by zero clojure.lang.Numbers.divide <span class="paren1">(<span class="code">Numbers.java:158</span>)</span>
|
|
user=></span></code></pre>
|
|
|
|
<p>I guess they got tired of people complaining about how the stack traces look
|
|
like shit, so they just... removed them? You can get them back with a bit of
|
|
work:</p>
|
|
|
|
<pre><code><span class="code">user=> <span class="paren1">(<span class="code">use '<span class="paren2">[<span class="code">clojure.stacktrace <span class="keyword">:only</span> <span class="paren3">[<span class="code">print-stack-trace</span>]</span></span>]</span></span>)</span>
|
|
user=> <span class="paren1">(<span class="code">print-stack-trace *e</span>)</span>
|
|
java.lang.ArithmeticException: Divide by zero
|
|
at clojure.lang.Numbers.divide <span class="paren1">(<span class="code">Numbers.java:158</span>)</span>
|
|
clojure.lang.Numbers.divide <span class="paren1">(<span class="code">Numbers.java:3808</span>)</span>
|
|
user$foo.invoke <span class="paren1">(<span class="code">form-init5869611501548592997.clj:1</span>)</span>
|
|
user$eval1219.invoke <span class="paren1">(<span class="code">form-init5869611501548592997.clj:1</span>)</span>
|
|
clojure.lang.Compiler.eval <span class="paren1">(<span class="code">Compiler.java:6782</span>)</span>
|
|
clojure.lang.Compiler.eval <span class="paren1">(<span class="code">Compiler.java:6745</span>)</span>
|
|
clojure.core$eval.invoke <span class="paren1">(<span class="code">core.clj:3081</span>)</span>
|
|
clojure.main$repl$read_eval_print__7099$fn__7102.invoke <span class="paren1">(<span class="code">main.clj:240</span>)</span>
|
|
clojure.main$repl$read_eval_print__7099.invoke <span class="paren1">(<span class="code">main.clj:240</span>)</span>
|
|
clojure.main$repl$fn__7108.invoke <span class="paren1">(<span class="code">main.clj:258</span>)</span>
|
|
clojure.main$repl.doInvoke <span class="paren1">(<span class="code">main.clj:258</span>)</span>
|
|
clojure.lang.RestFn.invoke <span class="paren1">(<span class="code">RestFn.java:1523</span>)</span>
|
|
clojure.tools.nrepl.middleware.interruptible_eval$evaluate$fn__623.invoke <span class="paren1">(<span class="code">interruptible_eval.clj:58</span>)</span>
|
|
clojure.lang.AFn.applyToHelper <span class="paren1">(<span class="code">AFn.java:152</span>)</span>
|
|
clojure.lang.AFn.applyTo <span class="paren1">(<span class="code">AFn.java:144</span>)</span>
|
|
clojure.core$apply.invoke <span class="paren1">(<span class="code">core.clj:630</span>)</span>
|
|
clojure.core$with_bindings_STAR_.doInvoke <span class="paren1">(<span class="code">core.clj:1868</span>)</span>
|
|
clojure.lang.RestFn.invoke <span class="paren1">(<span class="code">RestFn.java:425</span>)</span>
|
|
clojure.tools.nrepl.middleware.interruptible_eval$evaluate.invoke <span class="paren1">(<span class="code">interruptible_eval.clj:56</span>)</span>
|
|
clojure.tools.nrepl.middleware.interruptible_eval$interruptible_eval$fn__665$fn__668.invoke <span class="paren1">(<span class="code">interruptible_eval.clj:191</span>)</span>
|
|
clojure.tools.nrepl.middleware.interruptible_eval$run_next$fn__660.invoke <span class="paren1">(<span class="code">interruptible_eval.clj:159</span>)</span>
|
|
clojure.lang.AFn.run <span class="paren1">(<span class="code">AFn.java:22</span>)</span>
|
|
java.util.concurrent.ThreadPoolExecutor.runWorker <span class="paren1">(<span class="code">ThreadPoolExecutor.java:1142</span>)</span>
|
|
java.util.concurrent.ThreadPoolExecutor$Worker.run <span class="paren1">(<span class="code">ThreadPoolExecutor.java:617</span>)</span>
|
|
java.lang.Thread.run <span class="paren1">(<span class="code">Thread.java:745</span>)</span></span></code></pre>
|
|
|
|
<p>Oh yeah, <em>there's</em> the garbagepile I remember.</p>
|
|
|
|
<p>Lisp also hides the traceback at first, but it's easier to get at, and once you
|
|
do it's actually <em>helpful</em>:</p>
|
|
|
|
<pre><code><span class="code">CL-USER> <span class="paren1">(<span class="code">declaim <span class="paren2">(<span class="code">optimize <span class="paren3">(<span class="code">debug 3</span>)</span></span>)</span></span>)</span>
|
|
|
|
NIL
|
|
CL-USER> <span class="paren1">(<span class="code"><i><span class="symbol">defun</span></i> foo <span class="paren2">(<span class="code"></span>)</span> <span class="paren2">(<span class="code">/ 1 0</span>)</span></span>)</span>
|
|
<span class="comment">; in: DEFUN FOO
|
|
</span><span class="comment">; (/ 1 0)
|
|
</span><span class="comment">;
|
|
</span><span class="comment">; caught STYLE-WARNING:
|
|
</span><span class="comment">; Lisp error during constant folding:
|
|
</span><span class="comment">; arithmetic error DIVISION-BY-ZERO signalled
|
|
</span><span class="comment">; Operation was /, operands (1 0).
|
|
</span><span class="comment">;
|
|
</span><span class="comment">; compilation unit finished
|
|
</span><span class="comment">; caught 1 STYLE-WARNING condition
|
|
</span>
|
|
FOO
|
|
CL-USER> <span class="paren1">(<span class="code">foo</span>)</span>
|
|
|
|
debugger invoked on a DIVISION-BY-ZERO in thread
|
|
#<THREAD "main thread" RUNNING {1002C74703}>:
|
|
arithmetic error DIVISION-BY-ZERO signalled
|
|
Operation was /, operands <span class="paren1">(<span class="code">1 0</span>)</span>.
|
|
|
|
Type HELP for debugger help, or <span class="paren1">(<span class="code">SB-EXT:EXIT</span>)</span> to exit from SBCL.
|
|
|
|
restarts <span class="paren1">(<span class="code">invokable by number or by possibly-abbreviated name</span>)</span>:
|
|
0: [ABORT] Exit debugger, returning to top level.
|
|
|
|
<span class="paren1">(<span class="code">SB-KERNEL::INTEGER-/-INTEGER 1 0</span>)</span>
|
|
0] backtrace
|
|
|
|
Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {1002C74703}>
|
|
0: <span class="paren1">(<span class="code">SB-KERNEL::INTEGER-/-INTEGER 1 0</span>)</span>
|
|
1: <span class="paren1">(<span class="code">/ 1 0</span>)</span>
|
|
2: <span class="paren1">(<span class="code">FOO</span>)</span>
|
|
3: <span class="paren1">(<span class="code">SB-INT:SIMPLE-EVAL-IN-LEXENV <span class="paren2">(<span class="code">FOO</span>)</span> #<NULL-LEXENV></span>)</span>
|
|
4: <span class="paren1">(<span class="code">EVAL <span class="paren2">(<span class="code">FOO</span>)</span></span>)</span>
|
|
5: <span class="paren1">(<span class="code">INTERACTIVE-EVAL <span class="paren2">(<span class="code">FOO</span>)</span> <span class="keyword">:EVAL</span> NIL</span>)</span>
|
|
6: <span class="paren1">(<span class="code">SB-IMPL::REPL-FUN NIL</span>)</span>
|
|
7: <span class="paren1">(<span class="code"><span class="paren2">(<span class="code"><i><span class="symbol">LAMBDA</span></i> NIL <span class="keyword">:IN</span> SB-IMPL::TOPLEVEL-REPL</span>)</span></span>)</span>
|
|
8: <span class="paren1">(<span class="code">SB-IMPL::%WITH-REBOUND-IO-SYNTAX #<CLOSURE <span class="paren2">(<span class="code"><i><span class="symbol">LAMBDA</span></i> NIL <span class="keyword">:IN</span> SB-IMPL::TOPLEVEL-REPL</span>)</span> {1004A790FB}></span>)</span>
|
|
9: <span class="paren1">(<span class="code">SB-IMPL::TOPLEVEL-REPL NIL</span>)</span>
|
|
10: <span class="paren1">(<span class="code">SB-IMPL::TOPLEVEL-INIT</span>)</span>
|
|
11: <span class="paren1">(<span class="code"><span class="paren2">(<span class="code"><i><span class="symbol">FLET</span></i> <span class="keyword">#:WITHOUT-INTERRUPTS-BODY-86</span> <span class="keyword">:IN</span> SAVE-LISP-AND-DIE</span>)</span></span>)</span>
|
|
12: <span class="paren1">(<span class="code"><span class="paren2">(<span class="code"><i><span class="symbol">LABELS</span></i> SB-IMPL::RESTART-LISP <span class="keyword">:IN</span> SAVE-LISP-AND-DIE</span>)</span></span>)</span></span></code></pre>
|
|
|
|
<p>Notice how it actually warns me <em>at compile time</em> about the error, whereas
|
|
Clojure merrily compiles it without a peep. Also notice how the SBCL backtrace
|
|
gives me actual forms and arguments, instead of a line number (which is probably
|
|
inaccurate if you've been editing and eval'ing a file piecemeal).</p>
|
|
|
|
<h3 id="s24-old-bugs"><a href="index.html#s24-old-bugs">Old Bugs</a></h3>
|
|
|
|
<p>Running into <a href="http://dev.clojure.org/jira/browse/CLJ-700">four-year old bugs</a> was fun.</p>
|
|
|
|
<h3 id="s25-rip-your-heap"><a href="index.html#s25-rip-your-heap">RIP Your Heap</a></h3>
|
|
|
|
<p>Clojure's data structures and STM system make it pretty easy to write code that
|
|
both:</p>
|
|
|
|
<ul>
|
|
<li>Runs correctly</li>
|
|
<li>Generates enormous mountains of garbage</li>
|
|
</ul>
|
|
|
|
<p>This can be a good or bad thing, depending on how you look at it and what your
|
|
needs are.</p>
|
|
|
|
<p>Common Lisp has immutable data structures (via fset) and STM if you want them,
|
|
but it also lets you use efficient mutable things just as easily if you want.
|
|
It doesn't really encourage one specific way of doing things.</p>
|
|
|
|
<h2 id="s26-conclusion"><a href="index.html#s26-conclusion">Conclusion</a></h2>
|
|
|
|
<p>Ludum Dare 34 was a lot of fun. I'm definitely going to mark my calendar and do
|
|
the next one too.</p>
|
|
|
|
<p>If making a game sounds interesting, you should do it too! You don't need a ton
|
|
of programming experience, artistic skills, or anything but a love of video
|
|
games and a free weekend. Get out there and make a game!</p>
|
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html> |