emacs.d/clones/lisp/stevelosh.com/blog/2012/07/caves-of-clojure-03-4/index.html

297 lines
29 KiB
HTML
Raw Normal View History

2022-10-07 15:47:14 +02:00
<!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>The Caves of Clojure: Part 3.4 / 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'>The Caves of Clojure: Part 3.4</a></h1><p class='date'>Posted on July 11th, 2012.</p><p>This post is part of an ongoing series. If you haven't already done so, you
should probably start at <a href="../caves-of-clojure-01/index.html">the beginning</a>.</p>
<p>This entry (kind of) corresponds to <a href="http://trystans.blogspot.com/2011/08/roguelike-tutorial-03-scrolling-through.html">post three in Trystan's
tutorial</a>.</p>
<p>If you want to follow along, the code for the series is <a href="http://bitbucket.org/sjl/caves/">on Bitbucket</a> and
<a href="http://github.com/sjl/caves/">on GitHub</a>. Update to the <code>entry-03-4</code> tag to see the code as it stands
after this post.</p>
<ol class="table-of-contents"><li><a href="index.html#s1-summary">Summary</a></li><li><a href="index.html#s2-record-creation">Record Creation</a></li><li><a href="index.html#s3-update-in">update-in</a></li><li><a href="index.html#s4-namespaces">Namespaces</a></li><li><a href="index.html#s5-results">Results</a></li></ol>
<h2 id="s1-summary"><a href="index.html#s1-summary">Summary</a></h2>
<p>In the last post I said that the next post would be about Trystan's fourth
entry. I lied. I'm going to do a short entry about refactoring before I move
on, because I don't want to clutter up later ones with this stuff.</p>
<h2 id="s2-record-creation"><a href="index.html#s2-record-creation">Record Creation</a></h2>
<p>Hacker News user bitsai <a href="https://news.ycombinator.com/item?id=4220141">told me about</a> a new syntax for creating records in
Clojure 1.3, so I updated all the <code>(new Foo)</code> calls to use that.</p>
<p>One example is the <code>new-game</code> function. Before:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> new-game <span class="paren2">[<span class="code"></span>]</span>
<span class="paren2">(<span class="code">assoc <span class="paren3">(<span class="code">new Game nil <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">new UI <span class="keyword">:start</span></span>)</span></span>]</span> nil</span>)</span>
<span class="keyword">:location</span> <span class="paren3">[<span class="code">40 20</span>]</span></span>)</span></span>)</span></span></code></pre>
<p>After:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> new-game <span class="paren2">[<span class="code"></span>]</span>
<span class="paren2">(<span class="code">assoc <span class="paren3">(<span class="code">-&gt;Game nil <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:start</span></span>)</span></span>]</span> nil</span>)</span>
<span class="keyword">:location</span> <span class="paren3">[<span class="code">40 20</span>]</span></span>)</span></span>)</span></span></code></pre>
<p>It's certainly not too impressive from a &quot;characters saved&quot; point of view. But
this little change matters more than it might appear at first glance.</p>
<p>Imagine you define a record in Clojure:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns a</span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defrecord</span></i> Foo <span class="paren2">[<span class="code"></span>]</span></span>)</span></span></code></pre>
<p>And then in another namespace you want to use it:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns b
<span class="paren2">(<span class="code"><span class="keyword">:use</span> a</span>)</span></span>)</span>
<span class="paren1">(<span class="code">new Foo</span>)</span></span></code></pre>
<p>This will explode, because <code>Foo</code> is actually a Java class, so you need to import
it:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns b
<span class="paren2">(<span class="code"><span class="keyword">:import</span> a.Foo</span>)</span>
<span class="paren2">(<span class="code"><span class="keyword">:use</span> a</span>)</span></span>)</span>
<span class="paren1">(<span class="code">new Foo</span>)</span></span></code></pre>
<p>This is one example of Clojure's Java underpinnings leaking through.</p>
<p>In Clojure 1.3, <code>defrecord</code> will automatically generate a &quot;factory function&quot;
that creates the record, and you can <code>require</code> or <code>use</code> <em>that</em> like any other
function, so you don't need to screw around with a Java interop feature
(<code>import</code>) to use a pure Clojure feature.</p>
<p>This is a good thing. It means that progress is being made toward patching the
places that Java leaks into Clojure. It gives me hope that some day I'll feel
okay recommending Clojure to people without Java experience.</p>
<p>I updated all the <code>(new ...)</code> calls to use the new-style factory functions.
I won't paste them all here, but if you're following along you'll want to <code>grep
-R 'new ' .</code> and update the rest now.</p>
<h2 id="s3-update-in"><a href="index.html#s3-update-in">update-in</a></h2>
<p>Next is a tiny change that's just a bit cleaner. Alan Malloy <a href="https://twitter.com/alanmalloy/status/222748536595423232">told me</a> about
it. In the <code>process-input</code> function for the <code>:play</code> UI, the code to handle
smoothing the world looked like this:</p>
<pre><code><span class="code">\s <span class="paren1">(<span class="code">assoc game <span class="keyword">:world</span> <span class="paren2">(<span class="code">smooth-world <span class="paren3">(<span class="code"><span class="keyword">:world</span> game</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>This can be done much more cleanly using <code>update-in</code>:</p>
<pre><code><span class="code">\s <span class="paren1">(<span class="code">update-in game <span class="paren2">[<span class="code"><span class="keyword">:world</span></span>]</span> smooth-world</span>)</span></span></code></pre>
<p>Nice.</p>
<h2 id="s4-namespaces"><a href="index.html#s4-namespaces">Namespaces</a></h2>
<p>I said in an earlier post that I tend to leave things in one file until I feel
like they need to be pulled out. Well, that time has come.</p>
<p>First I pulled the UI drawing code into its own file: <code>ui/drawing.clj</code>. It
looks like this (nothing has changed, it's just in a file of its own now):</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.ui.drawing
<span class="paren2">(<span class="code"><span class="keyword">:require</span> <span class="paren3">[<span class="code">lanterna.screen <span class="keyword">:as</span> s</span>]</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code">def screen-size <span class="paren2">[<span class="code">80 24</span>]</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> clear-screen <span class="paren2">[<span class="code">screen</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code"><span class="paren4">[<span class="code">cols rows</span>]</span> screen-size
blank <span class="paren4">(<span class="code">apply str <span class="paren5">(<span class="code">repeat cols \space</span>)</span></span>)</span></span>]</span>
<span class="paren3">(<span class="code">doseq <span class="paren4">[<span class="code">row <span class="paren5">(<span class="code">range rows</span>)</span></span>]</span>
<span class="paren4">(<span class="code">s/put-string screen 0 row blank</span>)</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmulti</span></i> draw-ui
<span class="paren2">(<span class="code">fn <span class="paren3">[<span class="code">ui game screen</span>]</span>
<span class="paren3">(<span class="code"><span class="keyword">:kind</span> ui</span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> draw-ui <span class="keyword">:start</span> <span class="paren2">[<span class="code">ui game screen</span>]</span>
<span class="paren2">(<span class="code">s/put-string screen 0 0 <span class="string">&quot;Welcome to the Caves of Clojure!&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 1 <span class="string">&quot;Press any key to continue.&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 2 <span class="string">&quot;&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 3 <span class="string">&quot;Once in the game, you can use enter to win,&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 4 <span class="string">&quot;and backspace to lose.&quot;</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> draw-ui <span class="keyword">:win</span> <span class="paren2">[<span class="code">ui game screen</span>]</span>
<span class="paren2">(<span class="code">s/put-string screen 0 0 <span class="string">&quot;Congratulations, you win!&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 1 <span class="string">&quot;Press escape to exit, anything else to restart.&quot;</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> draw-ui <span class="keyword">:lose</span> <span class="paren2">[<span class="code">ui game screen</span>]</span>
<span class="paren2">(<span class="code">s/put-string screen 0 0 <span class="string">&quot;Sorry, better luck next time.&quot;</span></span>)</span>
<span class="paren2">(<span class="code">s/put-string screen 0 1 <span class="string">&quot;Press escape to exit, anything else to restart.&quot;</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> get-viewport-coords <span class="paren2">[<span class="code">game vcols vrows</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">location <span class="paren4">(<span class="code"><span class="keyword">:location</span> game</span>)</span>
<span class="paren4">[<span class="code">center-x center-y</span>]</span> location
tiles <span class="paren4">(<span class="code"><span class="keyword">:tiles</span> <span class="paren5">(<span class="code"><span class="keyword">:world</span> game</span>)</span></span>)</span>
map-rows <span class="paren4">(<span class="code">count tiles</span>)</span>
map-cols <span class="paren4">(<span class="code">count <span class="paren5">(<span class="code">first tiles</span>)</span></span>)</span>
start-x <span class="paren4">(<span class="code">- center-x <span class="paren5">(<span class="code">int <span class="paren6">(<span class="code">/ vcols 2</span>)</span></span>)</span></span>)</span>
start-x <span class="paren4">(<span class="code">max 0 start-x</span>)</span>
start-y <span class="paren4">(<span class="code">- center-y <span class="paren5">(<span class="code">int <span class="paren6">(<span class="code">/ vrows 2</span>)</span></span>)</span></span>)</span>
start-y <span class="paren4">(<span class="code">max 0 start-y</span>)</span>
end-x <span class="paren4">(<span class="code">+ start-x vcols</span>)</span>
end-x <span class="paren4">(<span class="code">min end-x map-cols</span>)</span>
end-y <span class="paren4">(<span class="code">+ start-y vrows</span>)</span>
end-y <span class="paren4">(<span class="code">min end-y map-rows</span>)</span>
start-x <span class="paren4">(<span class="code">- end-x vcols</span>)</span>
start-y <span class="paren4">(<span class="code">- end-y vrows</span>)</span></span>]</span>
<span class="paren3">[<span class="code">start-x start-y end-x end-y</span>]</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> draw-crosshairs <span class="paren2">[<span class="code">screen vcols vrows</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren3">[<span class="code">crosshair-x <span class="paren4">(<span class="code">int <span class="paren5">(<span class="code">/ vcols 2</span>)</span></span>)</span>
crosshair-y <span class="paren4">(<span class="code">int <span class="paren5">(<span class="code">/ vrows 2</span>)</span></span>)</span></span>]</span>
<span class="paren3">(<span class="code">s/put-string screen crosshair-x crosshair-y <span class="string">&quot;X&quot;</span> <span class="paren4">{<span class="code"><span class="keyword">:fg</span> <span class="keyword">:red}</span></span>)</span>
<span class="paren4">(<span class="code">s/move-cursor screen crosshair-x crosshair-y</span>)</span></span>)</span></span>)</span>
<span class="paren2">(<span class="code"><i><span class="symbol">defn</span></i> draw-world <span class="paren3">[<span class="code">screen vrows vcols start-x start-y end-x end-y tiles</span>]</span>
<span class="paren3">(<span class="code">doseq <span class="paren4">[<span class="code"><span class="paren5">[<span class="code">vrow-idx mrow-idx</span>]</span> <span class="paren5">(<span class="code">map vector
<span class="paren6">(<span class="code">range 0 vrows</span>)</span>
<span class="paren6">(<span class="code">range start-y end-y</span>)</span></span>)</span>
<span class="keyword">:let</span> <span class="paren5">[<span class="code">row-tiles <span class="paren6">(<span class="code">subvec <span class="paren1">(<span class="code">tiles mrow-idx</span>)</span> start-x end-x</span>)</span></span>]</span></span>]</span>
<span class="paren4">(<span class="code">doseq <span class="paren5">[<span class="code">vcol-idx <span class="paren6">(<span class="code">range vcols</span>)</span>
<span class="keyword">:let</span> <span class="paren6">[<span class="code"><span class="paren1">{<span class="code"><span class="keyword">:keys</span> <span class="paren2">[<span class="code">glyph color</span>]</span></span>}</span> <span class="paren1">(<span class="code">row-tiles vcol-idx</span>)</span></span>]</span></span>]</span>
<span class="paren5">(<span class="code">s/put-string screen vcol-idx vrow-idx glyph <span class="paren6">{<span class="code"><span class="keyword">:fg</span> color}</span>)</span></span>)</span></span>)</span></span>)</span>
<span class="paren3">(<span class="code"><i><span class="symbol">defmethod</span></i> draw-ui <span class="keyword">:play</span> <span class="paren4">[<span class="code">ui game screen</span>]</span>
<span class="paren4">(<span class="code"><i><span class="symbol">let</span></i> <span class="paren5">[<span class="code">world <span class="paren6">(<span class="code"><span class="keyword">:world</span> game</span>)</span>
tiles <span class="paren6">(<span class="code"><span class="keyword">:tiles</span> world</span>)</span>
<span class="paren6">[<span class="code">cols rows</span>]</span> screen-size
vcols cols
vrows <span class="paren6">(<span class="code">dec rows</span>)</span>
<span class="paren6">[<span class="code">start-x start-y end-x end-y</span>]</span> <span class="paren6">(<span class="code">get-viewport-coords game vcols vrows</span>)</span></span>]</span>
<span class="paren5">(<span class="code">draw-world screen vrows vcols start-x start-y end-x end-y tiles</span>)</span>
<span class="paren5">(<span class="code">draw-crosshairs screen vcols vrows</span>)</span></span>)</span></span>)</span>
<span class="paren3">(<span class="code"><i><span class="symbol">defn</span></i> draw-game <span class="paren4">[<span class="code">game screen</span>]</span>
<span class="paren4">(<span class="code">clear-screen screen</span>)</span>
<span class="paren4">(<span class="code">doseq <span class="paren5">[<span class="code">ui <span class="paren6">(<span class="code"><span class="keyword">:uis</span> game</span>)</span></span>]</span>
<span class="paren5">(<span class="code">draw-ui ui game screen</span>)</span></span>)</span>
<span class="paren4">(<span class="code">s/redraw screen</span>)</span></span>)</span></span></span></span></span></span></code></pre>
<p>And now I need to <code>use</code> the <code>draw-game</code> function back in <code>core.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.core
<span class="paren2">(<span class="code"><span class="keyword">:use</span> <span class="paren3">[<span class="code">caves.world <span class="keyword">:only</span> <span class="paren4">[<span class="code">random-world smooth-world</span>]</span></span>]</span>
<span class="paren3">[<span class="code">caves.ui.drawing <span class="keyword">:only</span> <span class="paren4">[<span class="code">draw-game</span>]</span></span>]</span></span>)</span>
<span class="paren2">(<span class="code"><span class="keyword">:require</span> <span class="paren3">[<span class="code">lanterna.screen <span class="keyword">:as</span> s</span>]</span></span>)</span></span>)</span></span></code></pre>
<p>The fact that I moved eleven top-level symbols into a new namespace and only had
to bring one of them back into the original is a pretty clear sign that this
chunk of code was ready to be moved into its own file.</p>
<p>I also did the same for the input processing code, moving it into
<code>ui/input.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.ui.input
<span class="paren2">(<span class="code"><span class="keyword">:use</span> <span class="paren3">[<span class="code">caves.world <span class="keyword">:only</span> <span class="paren4">[<span class="code">random-world smooth-world</span>]</span></span>]</span></span>)</span>
<span class="paren2">(<span class="code"><span class="keyword">:require</span> <span class="paren3">[<span class="code">lanterna.screen <span class="keyword">:as</span> s</span>]</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmulti</span></i> process-input
<span class="paren2">(<span class="code">fn <span class="paren3">[<span class="code">game input</span>]</span>
<span class="paren3">(<span class="code"><span class="keyword">:kind</span> <span class="paren4">(<span class="code">last <span class="paren5">(<span class="code"><span class="keyword">:uis</span> game</span>)</span></span>)</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> process-input <span class="keyword">:start</span> <span class="paren2">[<span class="code">game input</span>]</span>
<span class="paren2">(<span class="code">-&gt; game
<span class="paren3">(<span class="code">assoc <span class="keyword">:world</span> <span class="paren4">(<span class="code">random-world</span>)</span></span>)</span>
<span class="paren3">(<span class="code">assoc <span class="keyword">:uis</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:play</span></span>)</span></span>]</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> move <span class="paren2">[<span class="code"><span class="paren3">[<span class="code">x y</span>]</span> <span class="paren3">[<span class="code">dx dy</span>]</span></span>]</span>
<span class="paren2">[<span class="code"><span class="paren3">(<span class="code">+ x dx</span>)</span> <span class="paren3">(<span class="code">+ y dy</span>)</span></span>]</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> process-input <span class="keyword">:play</span> <span class="paren2">[<span class="code">game input</span>]</span>
<span class="paren2">(<span class="code">case input
<span class="keyword">:enter</span> <span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:win</span></span>)</span></span>]</span></span>)</span>
<span class="keyword">:backspace</span> <span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:lose</span></span>)</span></span>]</span></span>)</span>
\q <span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"></span>]</span></span>)</span>
\s <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:world</span></span>]</span> smooth-world</span>)</span>
\h <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">-1 0</span>]</span></span>)</span>
\j <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">0 1</span>]</span></span>)</span>
\k <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">0 -1</span>]</span></span>)</span>
\l <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">1 0</span>]</span></span>)</span>
\H <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">-5 0</span>]</span></span>)</span>
\J <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">0 5</span>]</span></span>)</span>
\K <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">0 -5</span>]</span></span>)</span>
\L <span class="paren3">(<span class="code">update-in game <span class="paren4">[<span class="code"><span class="keyword">:location</span></span>]</span> move <span class="paren4">[<span class="code">5 0</span>]</span></span>)</span>
game</span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> process-input <span class="keyword">:win</span> <span class="paren2">[<span class="code">game input</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren3">(<span class="code">= input <span class="keyword">:escape</span></span>)</span>
<span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"></span>]</span></span>)</span>
<span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:start</span></span>)</span></span>]</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defmethod</span></i> process-input <span class="keyword">:lose</span> <span class="paren2">[<span class="code">game input</span>]</span>
<span class="paren2">(<span class="code"><i><span class="symbol">if</span></i> <span class="paren3">(<span class="code">= input <span class="keyword">:escape</span></span>)</span>
<span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"></span>]</span></span>)</span>
<span class="paren3">(<span class="code">assoc game <span class="keyword">:uis</span> <span class="paren4">[<span class="code"><span class="paren5">(<span class="code">-&gt;UI <span class="keyword">:start</span></span>)</span></span>]</span></span>)</span></span>)</span></span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defn</span></i> get-input <span class="paren2">[<span class="code">game screen</span>]</span>
<span class="paren2">(<span class="code">assoc game <span class="keyword">:input</span> <span class="paren3">(<span class="code">s/get-key-blocking screen</span>)</span></span>)</span></span>)</span></span></code></pre>
<p>This isn't quite functional yet, because it needs the <code>-&gt;UI</code> factory function,
which is created by the <code>(defrecord UI [...])</code> that's still in <code>core.clj</code>.
I can't just <code>use</code> that in this file, because <code>core</code> is going to need to <code>use</code>
some functions from this, so I'd have circular imports.</p>
<p>The solution is to move the <code>(defrecord UI [...])</code> into a separate file.
I chose to put it in <code>ui/core.clj</code>:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.ui.core</span>)</span>
<span class="paren1">(<span class="code"><i><span class="symbol">defrecord</span></i> UI <span class="paren2">[<span class="code">kind</span>]</span></span>)</span></span></code></pre>
<p>And now I can pull its creation function back into the <code>input</code> namespace:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.ui.input
<span class="paren2">(<span class="code"><span class="keyword">:use</span> <span class="paren3">[<span class="code">caves.world <span class="keyword">:only</span> <span class="paren4">[<span class="code">random-world smooth-world</span>]</span></span>]</span>
<span class="paren3">[<span class="code">caves.ui.core <span class="keyword">:only</span> <span class="paren4">[<span class="code">-&gt;UI</span>]</span></span>]</span></span>)</span>
<span class="paren2">(<span class="code"><span class="keyword">:require</span> <span class="paren3">[<span class="code">lanterna.screen <span class="keyword">:as</span> s</span>]</span></span>)</span></span>)</span></span></code></pre>
<p>Finally I can update the <code>ns</code> back in the original <code>core.clj</code> to pull in the
functions I need, and remove the ones I don't:</p>
<pre><code><span class="code"><span class="paren1">(<span class="code">ns caves.core
<span class="paren2">(<span class="code"><span class="keyword">:use</span> <span class="paren3">[<span class="code">caves.ui.core <span class="keyword">:only</span> <span class="paren4">[<span class="code">-&gt;UI</span>]</span></span>]</span>
<span class="paren3">[<span class="code">caves.ui.drawing <span class="keyword">:only</span> <span class="paren4">[<span class="code">draw-game</span>]</span></span>]</span>
<span class="paren3">[<span class="code">caves.ui.input <span class="keyword">:only</span> <span class="paren4">[<span class="code">get-input process-input</span>]</span></span>]</span></span>)</span>
<span class="paren2">(<span class="code"><span class="keyword">:require</span> <span class="paren3">[<span class="code">lanterna.screen <span class="keyword">:as</span> s</span>]</span></span>)</span></span>)</span></span></code></pre>
<p>Whew!</p>
<h2 id="s5-results"><a href="index.html#s5-results">Results</a></h2>
<p>That was a lot of shuffling around, but now I've got five separate files, each
pertaining to one specific thing, instead of one big pile of code.</p>
<p>You can view the code <a href="https://github.com/sjl/caves/tree/entry-03-4/src/caves">on GitHub</a> if you want to see the end
result.</p>
<p>Next post I swear I'll add the player so we'll have an actual 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>