<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[Jimmy Theis]]></title>
<link href="http://jetheis.com/atom.xml" rel="self"/>
<link href="http://jetheis.com/"/>
<updated>2013-12-15T15:03:31-05:00</updated>
<id>http://jetheis.com/</id>
<author><name><![CDATA[Jimmy Theis]]></name></author>

<entry>
<title type="html"><![CDATA[Programming Interview Question: Eight Queens]]></title>
<link href="http://jetheis.com/blog/2013/12/01/programming-interview-question-eight-queens/"/>
<updated>2013-12-01T00:00:00-05:00</updated>
<id>http://jetheis.com/blog/2013/12/01/programming-interview-question-eight-queens</id>
<content type="html"><![CDATA[<p>This week&#39;s <a href="http://codingforinterviews.com/">Coding for Interviews</a> question is the <a href="https://en.wikipedia.org/wiki/Eight_queens_puzzle">Eight Queens Puzzle</a>, where the challenge is to find all the ways to place 8 queens on an 8x8 chess board, such that none of them are in a position to kill each other in the next move. You can see how tricky this can be if you <a href="http://www.brainmetrix.com/8-queens/">try to do it by hand</a> (unless you&#39;re Emilie, who found a solution in just a few minutes).</p>

<p>Here&#39;s my CoffeeScript solution. It&#39;s a depth-first search of board states (placing a queen in the next available position in the next unoccupied row for each new state), and it returns an array of &quot;solutions&quot;. Each solution is an array of indices representing the column in each row where a queen is to be placed (so <code>[1, 3]</code> represents queens at <code>(row 0, column 1)</code> and <code>(row 1, column 3)</code>):</p>

<!-- more -->

<div class="highlight"><pre><code class="coffeescript"><span class="nv">getPlacements = </span><span class="nf">(n) -&gt;</span>
    <span class="nv">fill = </span><span class="nf">(b, r, c) -&gt;</span>
        <span class="nv">valid = </span><span class="nf">(row, col) -&gt;</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="nx">row</span> <span class="o">&lt;</span> <span class="nx">n</span> <span class="o">and</span> <span class="mi">0</span> <span class="o">&lt;=</span> <span class="nx">col</span> <span class="o">&lt;</span> <span class="nx">n</span>
        <span class="nv">newBoard = </span><span class="p">(</span><span class="nx">row</span><span class="p">[..]</span> <span class="k">for</span> <span class="nx">row</span> <span class="k">in</span> <span class="nx">b</span><span class="p">)</span>
        <span class="nx">newBoard</span><span class="p">[</span><span class="nx">r</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="kc">no</span> <span class="k">for</span> <span class="nx">_</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
        <span class="nx">row</span><span class="p">[</span><span class="nx">c</span><span class="p">]</span> <span class="o">=</span> <span class="kc">no</span> <span class="k">for</span> <span class="nx">row</span> <span class="k">in</span> <span class="nx">newBoard</span>
        <span class="k">for</span> <span class="nx">i</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span>
            <span class="nx">newBoard</span><span class="p">[</span><span class="nx">r</span><span class="o">+</span><span class="nx">i</span><span class="p">][</span><span class="nx">c</span><span class="o">+</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">no</span> <span class="k">if</span> <span class="nx">valid</span> <span class="nx">r</span><span class="o">+</span><span class="nx">i</span><span class="p">,</span> <span class="nx">c</span><span class="o">+</span><span class="nx">i</span>
            <span class="nx">newBoard</span><span class="p">[</span><span class="nx">r</span><span class="o">-</span><span class="nx">i</span><span class="p">][</span><span class="nx">c</span><span class="o">+</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">no</span> <span class="k">if</span> <span class="nx">valid</span> <span class="nx">r</span><span class="o">-</span><span class="nx">i</span><span class="p">,</span> <span class="nx">c</span><span class="o">+</span><span class="nx">i</span>
            <span class="nx">newBoard</span><span class="p">[</span><span class="nx">r</span><span class="o">+</span><span class="nx">i</span><span class="p">][</span><span class="nx">c</span><span class="o">-</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">no</span> <span class="k">if</span> <span class="nx">valid</span> <span class="nx">r</span><span class="o">+</span><span class="nx">i</span><span class="p">,</span> <span class="nx">c</span><span class="o">-</span><span class="nx">i</span>
            <span class="nx">newBoard</span><span class="p">[</span><span class="nx">r</span><span class="o">-</span><span class="nx">i</span><span class="p">][</span><span class="nx">c</span><span class="o">-</span><span class="nx">i</span><span class="p">]</span> <span class="o">=</span> <span class="kc">no</span> <span class="k">if</span> <span class="nx">valid</span> <span class="nx">r</span><span class="o">-</span><span class="nx">i</span><span class="p">,</span> <span class="nx">c</span><span class="o">-</span><span class="nx">i</span>
        <span class="nx">newBoard</span>

    <span class="nv">openPositions = </span><span class="nf">(b, r) -&gt;</span>
        <span class="p">((</span><span class="k">if</span> <span class="nx">b</span><span class="p">[</span><span class="nx">r</span><span class="p">]</span><span class="o">?</span><span class="p">[</span><span class="nx">c</span><span class="p">]</span> <span class="k">then</span> <span class="nx">c</span> <span class="k">else</span> <span class="o">-</span><span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="nx">c</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]).</span><span class="nx">filter</span> <span class="nf">(c) -&gt;</span> <span class="nx">c</span> <span class="o">&gt;=</span> <span class="mi">0</span>

    <span class="nv">result = </span><span class="p">[]</span>
    <span class="nv">queue = </span><span class="p">[[[],</span> <span class="p">((</span><span class="kc">yes</span> <span class="k">for</span> <span class="nx">_</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="k">for</span> <span class="nx">_</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">])]]</span>

    <span class="k">while</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">length</span>
        <span class="p">[</span><span class="nx">placement</span><span class="p">,</span> <span class="nx">board</span><span class="p">]</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
        <span class="nx">result</span><span class="p">.</span><span class="nx">push</span> <span class="nx">placement</span> <span class="k">if</span> <span class="nx">placement</span><span class="p">.</span><span class="nx">length</span> <span class="o">is</span> <span class="nx">n</span>
        <span class="k">for</span> <span class="nx">c</span> <span class="k">in</span> <span class="nx">openPositions</span><span class="p">(</span><span class="nx">board</span><span class="p">,</span> <span class="nx">placement</span><span class="p">.</span><span class="nx">length</span><span class="p">).</span><span class="nx">reverse</span><span class="p">()</span>
            <span class="nv">newPlacement = </span><span class="nx">placement</span><span class="p">[..]</span>
            <span class="nx">newPlacement</span><span class="p">.</span><span class="nx">push</span> <span class="nx">c</span>
            <span class="nx">queue</span><span class="p">.</span><span class="nx">push</span> <span class="p">[</span><span class="nx">newPlacement</span><span class="p">,</span> <span class="nx">fill</span><span class="p">(</span><span class="nx">board</span><span class="p">,</span> <span class="nx">placement</span><span class="p">.</span><span class="nx">length</span><span class="p">,</span> <span class="nx">c</span><span class="p">)]</span>
    <span class="nx">result</span>
</code></pre></div>

<p>Just like last time, I spent a few hours refining my answer, so it&#39;s probably a lot more polished and succinct than one I&#39;d come up with in an interview environment. The basic strategy is the same either way, though: explore all of the possible &quot;next moves&quot; until you hit a point where there are no safe places for the next queen or you&#39;ve placed all N queens.</p>

<p>I also wrote a handy little printing function for calculating the solutions to an NxN board with N queens and printing them to a console in a readable format:</p>

<div class="highlight"><pre><code class="coffeescript"><span class="nv">printPlacements = </span><span class="nf">(n) -&gt;</span>
    <span class="nv">placements = </span><span class="nx">getPlacements</span> <span class="nx">n</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="p">((((</span><span class="k">if</span> <span class="nx">p</span><span class="o">?</span><span class="p">[</span><span class="nx">r</span><span class="p">]</span> <span class="o">==</span> <span class="nx">c</span> <span class="k">then</span> <span class="s">&#39;X&#39;</span> <span class="k">else</span> <span class="s">&#39;-&#39;</span><span class="p">)</span> <span class="o">\</span>
        <span class="k">for</span> <span class="nx">c</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]).</span><span class="nx">join</span> <span class="s">&#39;&#39;</span> <span class="o">\</span>
            <span class="k">for</span> <span class="nx">r</span> <span class="k">in</span> <span class="p">[</span><span class="mi">0</span><span class="p">..</span><span class="nx">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]).</span><span class="nx">join</span> <span class="s">&#39;\n&#39;</span> <span class="o">\</span>
                <span class="k">for</span> <span class="nx">p</span> <span class="k">in</span> <span class="nx">placements</span><span class="p">).</span><span class="nx">join</span> <span class="s">&#39;\n\n&#39;</span>
    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span> <span class="s">&quot;\nFound </span><span class="si">#{</span><span class="nx">placements</span><span class="p">.</span><span class="nx">length</span><span class="si">}</span><span class="s"> solutions&quot;</span>
</code></pre></div>

<p>The (snipped) output looks something like this:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">...

-------X
--X-----
X-------
-----X--
-X------
----X---
------X-
---X----

-------X
---X----
X-------
--X-----
-----X--
-X------
------X-
----X---

Found 92 solutions
</code></pre></div>
<p>And finally, a visualization of what the search actually looks like (slowed down about 10,000 times):</p>

<iframe width="320" height="320" frameborder="0" src="/embed/queens/"></iframe>

<p>I do have to say that as someone who sometimes conducts technical interviews, I&#39;m not sure I would actually give this question to an interviewee, unless I was leaving him or her alone to work for a couple of hours, because of how long it would probably take to reach an answer.</p>

<p>Doing the problem on a whiteboard would definitely help, because it would remove all of the tedious debugging, but even then the only candidates who would finish in a reasonable amount of time would be the ones who were already familiar with searching algorithms. That said, it&#39;s definitely a fun puzzle to solve, it would make a great homework problem in an algorithms class, and I had a lot of fun solving it myself.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Programming Interview Question: Level-Order Traversal]]></title>
<link href="http://jetheis.com/blog/2013/11/11/programming-interview-question-level-order-traversal/"/>
<updated>2013-11-11T00:00:00-05:00</updated>
<id>http://jetheis.com/blog/2013/11/11/programming-interview-question-level-order-traversal</id>
<content type="html"><![CDATA[<p>I&#39;ve been subscribed to Brian Jordan&#39;s <a href="http://codingforinterviews.com/">Coding for Interviews</a> weekly emails (which I highly recommend) for a while now, but I&#39;ve never written up an official solution to any of the weekly questions.</p>

<p>In the meantime, I&#39;ve become interested in learning more about <a href="http://coffeescript.org/">CoffeeScript</a> (a language that transcompiles to JavaScript), but have never found the time to sit down and really learn about it. So, in an effort to kill two birds with one stone, I&#39;m using CoffeeScript to solve last Friday&#39;s interview question:</p>

<!-- more -->

<blockquote>
This week's question is to print a binary tree of integers in level order.

Level-order printing means printing each row of the tree from left to right, from root row to the deepest row. After each row, print a newline.

For example:
</blockquote>

<p><img src="http://img.jetheis.com/ffe6d6c598e9fb13bf149de2976c25fd591aeca9.png" width="542" height="286" /></p>

<p>I honestly picked this question because it&#39;s one I already know <a href="https://en.wikipedia.org/wiki/Breadth-first_search#Algorithm">the answer to</a> (and probably so does everyone else who&#39;s taken an intro-level data structures class), so I knew I wouldn&#39;t spend a bunch of time just figuring out the algorithm.</p>

<p>Anyway, here&#39;s my eight-line solution in CoffeeScript:</p>

<div class="highlight"><pre><code class="coffeescript"><span class="nv">levelOrder = </span><span class="nf">(rootNode) -&gt;</span>
    <span class="nv">queue = </span><span class="p">[[</span><span class="nx">rootNode</span><span class="p">,</span> <span class="mi">0</span><span class="p">]]</span>
    <span class="p">(</span><span class="k">while</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">length</span>
        <span class="p">[</span><span class="nx">node</span><span class="p">,</span> <span class="nx">level</span><span class="p">]</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">pop</span><span class="p">()</span>
        <span class="nx">queue</span><span class="p">.</span><span class="nx">unshift</span> <span class="p">[</span><span class="nx">node</span><span class="p">.</span><span class="nx">left</span><span class="p">,</span> <span class="nx">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="nx">node</span><span class="p">.</span><span class="nx">left</span><span class="o">?</span>
        <span class="nx">queue</span><span class="p">.</span><span class="nx">unshift</span> <span class="p">[</span><span class="nx">node</span><span class="p">.</span><span class="nx">right</span><span class="p">,</span> <span class="nx">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="k">if</span> <span class="nx">node</span><span class="p">.</span><span class="nx">right</span><span class="o">?</span>
        <span class="nx">node</span><span class="p">.</span><span class="nx">val</span> <span class="o">+</span> <span class="k">if</span> <span class="nx">queue</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">?</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">&gt;</span> <span class="nx">level</span> <span class="k">then</span> <span class="s">&#39;\n&#39;</span> <span class="k">else</span> <span class="s">&#39;&#39;</span>
    <span class="p">).</span><span class="nx">join</span><span class="p">(</span><span class="s">&#39; &#39;</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\n\ /g</span><span class="p">,</span> <span class="s">&#39;\n&#39;</span><span class="p">)</span>
</code></pre></div>

<p>Some of the language features used here include <a href="http://coffeescript.org/#loops">while loops as expressions</a>, <a href="http://coffeescript.org/#conditionals">conditionals as expressions</a>, <a href="http://coffeescript.org/#destructuring">destructuring assignment</a>, and <a href="http://coffeescript.org/#operators">the existential operator</a>.</p>

<p>For anyone who&#39;s interested, here&#39;s the 20 or so lines of JavaScript my code transcompiles to (slightly reformatted):</p>

<div class="highlight"><pre><code class="javascript"><span class="kd">var</span> <span class="nx">levelOrder</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">rootNode</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">var</span> <span class="nx">level</span><span class="p">,</span> <span class="nx">node</span><span class="p">,</span> <span class="nx">queue</span><span class="p">;</span>
    <span class="nx">queue</span> <span class="o">=</span> <span class="p">[[</span><span class="nx">rootNode</span><span class="p">,</span> <span class="mi">0</span><span class="p">]];</span>
    <span class="k">return</span> <span class="p">((</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">_ref</span><span class="p">,</span> <span class="nx">_ref1</span><span class="p">,</span> <span class="nx">_results</span><span class="p">;</span>
        <span class="nx">_results</span> <span class="o">=</span> <span class="p">[];</span>
        <span class="k">while</span> <span class="p">(</span><span class="nx">queue</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
            <span class="nx">_ref</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">pop</span><span class="p">(),</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">_ref</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="nx">level</span> <span class="o">=</span> <span class="nx">_ref</span><span class="p">[</span><span class="mi">1</span><span class="p">];</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">left</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">queue</span><span class="p">.</span><span class="nx">unshift</span><span class="p">([</span><span class="nx">node</span><span class="p">.</span><span class="nx">left</span><span class="p">,</span> <span class="nx">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span>
            <span class="p">}</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">right</span> <span class="o">!=</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
                <span class="nx">queue</span><span class="p">.</span><span class="nx">unshift</span><span class="p">([</span><span class="nx">node</span><span class="p">.</span><span class="nx">right</span><span class="p">,</span> <span class="nx">level</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]);</span>
            <span class="p">}</span>
            <span class="nx">_results</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">val</span> <span class="o">+</span> <span class="p">(((</span><span class="nx">_ref1</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">?</span>
                <span class="nx">_ref1</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">:</span> <span class="k">void</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&gt;</span> <span class="nx">level</span> <span class="o">?</span> <span class="s1">&#39;\n&#39;</span> <span class="o">:</span> <span class="s1">&#39;&#39;</span><span class="p">));</span>
        <span class="p">}</span>
        <span class="k">return</span> <span class="nx">_results</span><span class="p">;</span>
    <span class="p">})()).</span><span class="nx">join</span><span class="p">(</span><span class="s1">&#39; &#39;</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\n\ /g</span><span class="p">,</span> <span class="s1">&#39;\n&#39;</span><span class="p">);</span>
<span class="p">};</span>
</code></pre></div>

<p>I was surprised to see how readable the output code was, which further enforces the idea that CoffeeScript really is just &quot;better&quot; JavaScript (or as the website puts it, &quot;an attempt to expose the good parts of JavaScript in a simple way&quot;).</p>

<p>I had a lot of fun using a new language to solve an interview question, and I think I learned a lot about CoffeeScript in the process. I&#39;m not sure if I&#39;ll make a habit of this or not; I guess we&#39;ll see when the next interview question email comes out.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Blog Relaunch]]></title>
<link href="http://jetheis.com/blog/2013/11/03/blog-relaunch/"/>
<updated>2013-11-03T00:00:00-04:00</updated>
<id>http://jetheis.com/blog/2013/11/03/blog-relaunch</id>
<content type="html"><![CDATA[<p>Well, it&#39;s been a while (7 months or so) since I&#39;ve posted anything. In the meantime, I&#39;ve been pretty busy. My work fills up a lot of my time, and I&#39;ve really enjoyed what I&#39;ve been working on for the past few months, so I let that be my primary focus. As the seasons change, though, I realize that I&#39;ve missed writing blog posts (even if nobody reads them). So, I&#39;m taking up blogging again.</p>

<!-- more -->

<p>I actually came to this conclusion a few weeks ago, but the current look-and-feel of my blog bothered me so much that I felt like I had to change it before I started writing again. I started by attempting to create a new, super-simplified theme for <a href="http://octopress.org/">Octopress</a> (the static blog framework I had been using for a couple of years), but the more I customized, the more I felt like I was just making a theme to turn an Octopress blog into a non-Octopress blog.</p>

<p>I eventually decided to start a blog &quot;from scratch,&quot; using just <a href="http://jekyllrb.com/">Jekyll</a> (the static site generator that Octopress is built on). I&#39;m really glad I made that decision, because the last couple of weeks have been an awesome experience of building my blog from the ground up and having control over every last detail of it without having to make weird hacks in other people&#39;s code. Because I stuck with more or less the same site engine, and because my posts are still written in <a href="https://en.wikipedia.org/wiki/Markdown">Markdown</a>, it was pretty easy to migrate my old posts over, too.</p>

<p>A few more things I changed, for those who are interested:</p>

<ul>
<li>I stopped hosting my site on <a href="http://pages.github.com/">GitHub Pages</a>. Instead, I&#39;m now using pure <a href="http://aws.amazon.com/s3/">Amazon S3</a>, which lets me control things like gzip compression, redirects, etc.</li>
<li>Since I&#39;m hosting an S3 site from my root domain, I&#39;ve also started using <a href="http://aws.amazon.com/route53/">Amazon Route 53</a> for my DNS, instead of NameCheap&#39;s built-in DNS. This was more a technical requirement of moving to S3, but I thought I&#39;d mention it anyway.</li>
<li>I now host all of my images from <code>img.jetheis.com</code>, which is a <a href="http://aws.amazon.com/cloudfront/">Amazon CloudFront</a> site, backed by an S3 bucket.</li>
<li>Because I stopped using GitHub pages, I don&#39;t need a private GitHub repo anymore, so I switched to <a href="https://bitbucket.org/">BitBucket</a>. I know it&#39;s always been a second-class citizen to GitHub, but we use a lot of <a href="https://www.atlassian.com/">Atlassian</a> products at my work and love all of them. I&#39;ve been similarly impressed by BitBucket, and have even started using <a href="https://www.atlassian.com/software/sourcetree/overview">SourceTree</a> for managing my commits and pushes.</li>
<li>I&#39;m <strong>looking</strong> at moving away from <a href="http://www.google.com/intx/en/enterprise/apps/business/">Google Apps</a> for my email hosting, since they&#39;re phasing out free accounts. Right now the front-runner is <a href="https://www.zoho.com/mail/">Zoho Mail Lite</a>, which looks like it has all the features I use from Google Apps. If I do end up switching, I&#39;ll post about it.</li>
</ul>

<p>As always, thanks for reading.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[A Follow-Up on Energy Savings]]></title>
<link href="http://jetheis.com/blog/2013/03/24/a-follow-up-on-energy-savings/"/>
<updated>2013-03-24T14:38:00-04:00</updated>
<id>http://jetheis.com/blog/2013/03/24/a-follow-up-on-energy-savings</id>
<content type="html"><![CDATA[<p>A couple of months ago, I <a href="/blog/2013/01/18/going-a-little-green-ish/">posted</a> about a few of measures I had taken to try to reduce the utility costs in my apartment. Now that it&#39;s been a few months, I think I owe a report (even if it&#39;s sort of an inconclusive one).</p>

<p>I started the power-saving measures around January 10, 2013. Since then, I have received my power bills for 12/23 - 1/21 (29 days) and  1/21 - 2/21 (31 days). The first covers 1095 <a href="http://en.wikipedia.org/wiki/Heating_degree_day">degree days</a>, and the second 1092. This is a surprisingly close comparison (better than I thought I&#39;d get) between two months, even though one month contained the end of a holiday season and the other one is a little longer.</p>

<!-- more -->

<p>Anyway, here are the results:</p>

<ul>
<li>12/23 - 1/21 (29 days, 1095 degree days): $188.77</li>
<li>1/21 - 2/21 (31 days, 1092 degree days): $178.17</li>
</ul>

<p>So at the very surface level, it seems safe to say that the steps I took did in fact save me money. I honestly expected that, because it would be very hard to believe that:</p>

<ul>
<li>a cold apartment loses more heat energy than a warm apartment, or</li>
<li>light bulbs that require 22% as much power as their predecessors would somehow use more power.</li>
</ul>

<p>With that said, the one thing that&#39;s really hard to actually quantify is how <em>much</em> power this is saving.</p>

<p>We can look at the number above and say I saved about $10.00 from January to February, and since I implemented the saving measures two-thirds of the way through the January billing cycle, that number should be more like $15.00, but that involves too many assumptions to really say with any confidence.</p>

<p>The problem is that I don&#39;t spend a consistent amount of time in my apartment each month, and neither does Emilie. If there&#39;s someone in the apartment during the day, that&#39;s 8 or more additional hours the heat is turned on, and probably some lights along with it. So the number of vacation days, weekends in Bloomington, visits from Emilie, and pretty much any other disruptions to a &quot;normal&quot; schedule significantly change the amount of power I use day to day. The same is true for water, where I&#39;ve also seen a $10-$15 savings month-to-month, with a similarly wide variance in usage.</p>

<p>Even with my pretty unscientific findings, I&#39;d like to add some closure to this whole thing, so here goes:</p>

<p>The Nest thermostat is a pretty cool gadget, and I&#39;d recommend it for anyone who thinks it&#39;s neat and wants to try it out. If you just want to save money, there are <a href="https://www.google.com/search?q=programmable+thermostats">plenty of cheaper options</a> that give you comparable control over your schedule.</p>

<p>Replacing your light bulbs is kind of a no-brainer in my opinion. After just a day or two with the new CFL bulbs, I honestly couldn&#39;t tell the difference (the color seemed a little redder initially). The only caveat with the standard issue CFL bulbs is that they tend not to support dimmers, so if you&#39;re a fan of dimmers, you&#39;ll have a slightly harder choice to make between the more expensive CFL bulbs and standard incandescents (or an even bigger investment in LED bulbs).</p>

<p>Taking shorter showers is another no-brainer. It gives you more time in the morning, saves hot water for others, and saves money. It&#39;s not even really worth talking much about.</p>

<p>Anyway, that concludes the update I feel I owed to anyone who read the initial post. If the Nest performs surprisingly well or poorly during the summer months, I may update again with that information, but I imagine the results will be more of the same: a more managed heating and cooling schedule is a little better at keeping costs down.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Using Dropbox as a Private GitHub]]></title>
<link href="http://jetheis.com/blog/2013/02/17/using-dropbox-as-a-private-github/"/>
<updated>2013-02-17T10:19:00-05:00</updated>
<id>http://jetheis.com/blog/2013/02/17/using-dropbox-as-a-private-github</id>
<content type="html"><![CDATA[<p>This may be old news by now, but I only just realized how great it is, so consider this my rebroadcasting of the idea:</p>

<p><a href="https://github.com/">GitHub</a> has now become the preferred way to manage source code for programming projects. There&#39;s good reason for this: it&#39;s a really wonderful site with a great following. However, there are times when it&#39;s really not desirable to have a project completely public. GitHub provides some <a href="https://github.com/plans">paid options</a> for this, as well as <a href="https://github.com/edu">educational accounts</a> (which you should really check out if you&#39;re a student or instructor). If you&#39;ve used up your free repositories or simply don&#39;t think the project at hand warrants spending money on, you need another option.</p>

<!-- more -->

<p>That&#39;s where <a href="https://www.dropbox.com/">Dropbox</a> comes in. Dropbox is a really great backup/sync/file sharing service that pretty seamlessly syncs files between computers. Some time ago, Dropbox added the ability to <a href="https://www.dropbox.com/help/11/en">restore old versions of files</a>. For really simple projects, this may very well be all the version control you need. If that&#39;s the case, by all means stop reading this post and start, uh, &quot;drop-box-ing&quot;.</p>

<p>For programming projects, this level of version tracking more often than not isn&#39;t enough. That&#39;s why we use GitHub. My proposal for situations where neither GitHub nor Dropbox is a good fit (and what many people are already doing) is this: let&#39;s set up bare Git repositories in our Dropbox folders and use them as our Git remotes.</p>

<p>Let me explain. A &quot;bare&quot; Git repository is one that doesn&#39;t actually have any of the repository&#39;s files shown. Instead, it just contains the guts/mechanics of the repository (the stuff that&#39;s usually in the <code>.git</code> directory). This is the type of repository GitHub and others use, as it&#39;s only meant to be a place to push and pull from, not to do any direct file edits or commits from.</p>

<p>So let&#39;s go through the process of setting up what I&#39;m talking about. First, in your Dropbox folder, create a place for all your repositories to live (mine is a top-level folder named &quot;Git&quot;):</p>

<p><img src="http://img.jetheis.com/1e585ce57a9c9c8e017d2b612a18c316f34ece26.png" width="589" height="431" /></p>

<p>Now, in a Terminal, <code>cd</code> into that folder and create a bare repository:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cd ~/Dropbox/Git
$ git init --bare mytestrepo.git
  Initialized empty Git repository in /Users/jetheis/Dropbox/Git/mytestrepo.git/
</code></pre></div>
<p>You should see the new repository (it&#39;s just a folder) show up now:</p>

<p><img src="http://img.jetheis.com/b5ce6a6ff5df2845b30f30a1a7ba6df8edcb47d4.png" width="589" height="431" /></p>

<p>Inside, you&#39;ll see the guts of the repo:</p>

<p><img src="http://img.jetheis.com/67b768b1b7f6dc3d1073c5e3a09464f1172392e3.png" width="589" height="431" /></p>

<p>That&#39;s really all you need to do to get the bare repository set up. Let&#39;s try using it now. First let&#39;s make a sample project with a couple of commits:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cd ~
$ mkdir testrepo
$ cd testrepo
$ git init
  Initialized empty Git repository in /Users/jetheis/testrepo/.git/
$ echo &quot;Some Text&quot; &gt; testfile.txt
$ git add testfile.txt
$ git commit -m &quot;Add a test file&quot;
  [master (root-commit) 0220f05] Add a test file
   1 file changed, 1 insertion(+)
   create mode 100644 testfile.txt
$ echo &quot;More text&quot; &gt;&gt; testfile.txt
$ git commit testfile.txt -m &quot;Add more text&quot;
  [master f014b59] Add more text
   1 file changed, 1 insertion(+)
</code></pre></div>
<p>We&#39;ll check out the log and file content (to verify against in a second):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cat testfile.txt
  Some Text
  More text
$ git log
  commit f014b59d8d3af6e20bd5d671bd98f86e26fba743
  Author: Jimmy Theis
  Date:   Sun Feb 17 12:10:19 2013 -0500

      Add more text

  commit 0220f050e087d92ad96a549c2b913363f551b7fc
  Author: Jimmy Theis
  Date:   Sun Feb 17 12:08:50 2013 -0500

      Add a test file
</code></pre></div>
<p>Now we can hook up our new bare repository as an upstream for this one:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ git remote add origin ~/Dropbox/Git/mytestrepo.git
</code></pre></div>
<p>And push to it:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ git push -u origin master
  Counting objects: 6, done.
  Delta compression using up to 4 threads.
  Compressing objects: 100% (2/2), done.
  Writing objects: 100% (6/6), 458 bytes, done.
  Total 6 (delta 0), reused 0 (delta 0)
  Unpacking objects: 100% (6/6), done.
  To /Users/jetheis/Dropbox/Git/mytestrepo.git
   * [new branch]      master -&gt; master
  Branch master set up to track remote branch master from origin.
</code></pre></div>
<p>You should see your Dropbox icon spin for a second while it syncs the files for you, though you&#39;ll still notice that inside your Dropbox version of the repository, you still don&#39;t see any <code>testfile.txt</code>, because again, only the guts of the repository appear here.</p>

<p>Let&#39;s try cloning from the bare repository to make sure our files and content are actually stored there. On the same computer (or another one that Dropbox syncs to), clone the repository:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cd ~
$ git clone ~/Dropbox/Git/mytestrepo.git testrepo2
  Cloning into &#39;testrepo2&#39;...
  done.
</code></pre></div>
<p>Now we&#39;ll make sure we have both our current files and our history:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cd testrepo2
$ ls
  testfile.txt      
$ cat testfile.txt
  Some Text
  More text
$ git log
  commit f014b59d8d3af6e20bd5d671bd98f86e26fba743
  Author: Jimmy Theis
  Date:   Sun Feb 17 12:10:19 2013 -0500

      Add more text

  commit 0220f050e087d92ad96a549c2b913363f551b7fc
  Author: Jimmy Theis
  Date:   Sun Feb 17 12:08:50 2013 -0500

      Add a test file
</code></pre></div>
<p>No surprise, everything is reflected, just like we expected. We can now push and pull (the <code>origin</code> remote will already be hooked up since we cloned from the bare repository, so <code>git pull</code> and <code>git push</code> are all we need to run) and have those changes synced to all Dropbox-connected computers. Pretty cool, huh?</p>

<p>The really cool part is using Dropbox&#39;s sharing features with these repositories. Using the Dropbox website, you can either share your whole &quot;Git&quot; folder with others, or, because each repository is a folder, just share a single project:</p>

<p><img src="http://img.jetheis.com/c3016231a0c5d59e1c427980fb99a438e3450562.png" width="560" height="57" /></p>

<p>A quick note about problems before we go:</p>

<p>If you try to pull or push while files are still syncing, or if something gets messed up in the repository, you may see an error message like this:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">error: unable to find ea2ae105b8955b3f73d79065bc52ee3126cd2e4f
error: refs/heads/master does not point to a valid object!
Your configuration specifies to merge with the ref &#39;master&#39;
from the remote, but no such ref was fetched.
</code></pre></div>
<p>The error may vary depending on what&#39;s gone wrong, but the solution in any case is the same: wait until Dropbox finishes syncing to make sure it&#39;s not just out-of-date files, then if the problem still exists, delete your Dropbox repository (mytestrepo.git) and create a new bare repository with the same name in its place:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ cd ~/Dropbox/Git
$ rm -rf mytestrepo.git
$ git init --bare mytestrepo.git
  Initialized empty Git repository in /Users/jetheis/Dropbox/Git/mytestrepo.git/
</code></pre></div>
<p>Then push to it from the most up-to-date copy of the code:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ git push origin master
  Counting objects: 9, done.
  Delta compression using up to 4 threads.
  Compressing objects: 100% (4/4), done.
  Writing objects: 100% (9/9), 706 bytes, done.
  Total 9 (delta 0), reused 0 (delta 0)
  Unpacking objects: 100% (9/9), done.
  To /Users/jetheis/Dropbox/Git/mytestrepo.git
   * [new branch]      master -&gt; master
</code></pre></div>
<p>That should restore your repository state (as long as the pushing copy of the code was entirely up-to-date).</p>

<p>Again, this is probably old news for some, but it&#39;s definitely worth some lip service for anyone it hasn&#39;t occurred to yet.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Going a Little Green(ish)]]></title>
<link href="http://jetheis.com/blog/2013/01/18/going-a-little-green-ish/"/>
<updated>2013-01-18T08:33:00-05:00</updated>
<id>http://jetheis.com/blog/2013/01/18/going-a-little-green-ish</id>
<content type="html"><![CDATA[<p>A little background about me may be useful in understanding where I&#39;m coming from in this post: for basically all of college I was, frankly, really poor. I was always able to at least buy cheap food and drive to Bloomington to see Emilie, but sometimes not too much more than that.</p>

<p>When I lived in an off-campus apartment during my senior year, this tightness of money motivated some sort of extreme behavior, including my roommate unscrewing several of his vanity lights and the apartment reaching pretty uncomfortable temperatures before any heating or air conditioning was turned on.</p>

<p>I was unhappy living with such present reminders of my own lack of funds, so after graduation I reacted to my new full-time job and nice apartment by doing basically the opposite: making my apartment as comfortable as possible, and just budgeting a higher amount of money for utilities. After all, it would be sort of silly to be shivering in my own apartment when I can easily afford to heat the place.</p>

<!-- more -->

<p>After about seven months of this style of living, I noticed that my utilities budget has been bumped up several times and has nearly grown to the size of my grocery budget.</p>

<p>This bothered me for a while, so I started considering energy saving options to possibly cut down that cost and free up some more funds. Still in the &quot;new job&quot; mindset perhaps, I did hold on to a sort of <em>prime directive</em>, which is this:</p>

<p>Since I can afford to pay for my utilities, and since I value my comfort at least as much as the money high utilities would cost me, any energy saving ideas I implement are not allowed to make my life any less comfortable.</p>

<p>It seems selfish, and it is, but it also seems pretty fair. I value my comfort as least as high as my current utility bills, so it doesn&#39;t make sense to sacrifice it to make them lower. So with that background, let me explain what I&#39;ve done to try to save some clams behind the scenes.</p>

<h2>The Thermostat</h2>

<p>My first purchase, which was really the one that kick-started my interest in cutting these energy costs, was a <a href="http://www.nest.com/">Nest Thermostat</a>. It was a total impulse buy at Lowe&#39;s, but I&#39;m really glad I did it. It is still just a thermostat, but it does bring some pretty sweet features to the table, some of which are aimed at saving money.</p>

<p>To start with, Nest connects itself to your home wifi, allowing you to control your thermostat through the Nest.com web app, or through the Nest <a href="https://itunes.apple.com/us/app/nest-mobile/id464988855">iOS</a> or <a href="https://play.google.com/store/apps/details?id=com.nestlabs.android">Android</a> apps. Being able to control the thermostat remotely may be one of the biggest cost savers, at least to someone like me, because I&#39;m happy to turn down the heat in my apartment when I&#39;m gone; I just don&#39;t want to have to sit in a cold apartment for an hour while it warms back up.</p>

<p>The other big advantages Nest has are its <a href="http://support.nest.com/article/How-do-I-program-a-heating-and-cooling-schedule">easy-to-design schedule</a> and its <a href="http://support.nest.com/article/What-is-Auto-Away">Auto-Away</a> mode. Between the two of these, it&#39;s very difficult for me to actually leave the heat on when I&#39;m out of my apartment for any significant length of time.</p>

<p>I&#39;m very hopeful that this gadget will save me some money, though it&#39;s yet to be determined how long it will take to pay for itself.</p>

<p>Here&#39;s what it looks like in my apartment (you can see it cooling down for the night here):</p>

<p><img src="http://img.jetheis.com/e8ff2aadbb5f85130d0607c6c47f2dfec182469b.png" width="400" height="300" /></p>

<h2>The Lights</h2>

<p>As I considered how much money my new thermostat would save me, it occurred to that there may be other optimizable things about my apartment, namely the lights. I had always thought (and still do) that living in near darkness was sort of silly for the small amount of money it saves, but I hadn&#39;t considered what sort of difference it would make to make my existing lights more efficient, regardless of their usage.</p>

<p>A quick count around my apartment gives me 26 60-watt light bulbs that are almost always turned on, 3 more that are rarely turned on, and 2 in the laundry room that are almost never turned on. Twenty-six lights seems like a ton, but when each light fixture hold multiple bulbs and your apartment has lots of nooks and crevices, having the main areas lit means having lots of lights on.</p>

<p>Anyway, if we just look at the 26 bulbs that are on when I&#39;m home in the evening (from about 5:30 to about 10:30, 5 hours), those bulbs use around <a href="http://www.wolframalpha.com/input/?i=60+Watts+*+26+*+5+hours+in+kWh">7.8 kWh per day</a>, at $0.108662 per kWh (I know, <a href="http://www.bremc.com/">Boone REMC</a> is expensive), my regular lights cost me something like <a href="http://www.wolframalpha.com/input/?i=7.8+kWh+at+%240.108662+per+kWh">$0.85 per day</a>, and that&#39;s just on weekdays when I&#39;m only home in the evening.</p>

<p>Now, let&#39;s consider replacing each 60-watt bulb with its 13-watt <a href="http://en.wikipedia.org/wiki/Compact_fluorescent_lamp">CFL</a> equivalent. Using the same math, such a replacement set of bulbs would only cost me about <a href="http://www.wolframalpha.com/input/?i=%2813+watts+*+26+*+5+hours%29+at+%240.108662+per+kilowatt+hour">$0.18 per weekday</a>, a $0.67 savings. That doesn&#39;t seem like much, but over the <a href="http://www.wolframalpha.com/input/?i=week+days+in+january">22 or so weekdays per month</a>, that&#39;s $14.70 in monthly savings, not even considering weekends.</p>

<p>As the above math shows, it seemed totally worth it to spend some money replacing the lights in my apartment. After a little research (and admittedly buying the wrong ones), I realized that it&#39;s <em>really important</em> to buy the <em>Soft White</em> CFL bulbs. Otherwise they look blue and your home looks like a hospital. With this new knowledge, I ended up buying <a href="http://www.lowes.com/pd_306204-371-60073_0__">these lights</a> from Lowe&#39;s, though I also had to buy a smaller half-pack to go with it, as well as few with smaller bases for my ceiling fans.</p>

<p>All in all, I spent about $50 replacing the light bulbs in my apartment. I can hardly tell the difference in how my lighting looks (if anything, it&#39;s brighter in here), so I&#39;m very excited to see what kind of money this ends up saving me.</p>

<p>(Oh yeah, I also ended up throwing away a ton of old light bulbs and making quite a racket in the dumpster)</p>

<p><img src="http://img.jetheis.com/f5473c1460b94fd1dbabdf395c36e4c72038a859.png" width="400" height="300" /></p>

<h2>The Water</h2>

<p>The final change I made is technically breaking the rules I set for myself, but I&#39;m really okay with it.</p>

<p>For a long time, I&#39;ve just taken as long of a shower as I wanted, turning the water off basically when I got too bored or too hot. It&#39;s a great time to think, and I valued that along with the comfort of the hot water.</p>

<p>My shower head is marked as a <em>2.5 GPM</em> (gallons per minute), so if we consider reducing a 30-minute shower to a 10-minute shower, once per day, that could possibly reduce my water usage by up to <a href="http://www.wolframalpha.com/input/?i=%282.5+gallons+per+minute+for+20+minutes%29+*+30">1500 gallons</a> (I use something like 2500 gallons most months).</p>

<p>Now in practice, I&#39;m sure it won&#39;t be quite that much, especially if the shower head isn&#39;t outputting its full volume every minute, but the numbers above are at least interesting enough to warrant giving it a try.</p>

<p>So, for the last couple of weeks I&#39;ve been trying to keep my showers to around 10 minutes, which is actually harder than I thought since I so naturally slide into a daydreaming mindset while I&#39;m in there. I&#39;m very interested to see what a difference making a conscious effort has. If it does significantly reduce my water costs (which are pretty high since my water and sewer are provided my the neighboring town of Whitestown, not Zionsville), I&#39;m sure it will motivate me to be even stricter about my shower times.</p>

<p>The main reason I&#39;m okay with this change even though it does introduce some discomfort is because I now really value the extra time taking a short shower allows me in the morning. This makes it less an issue of being afraid to take showers and more an issue of good time management with a side-effect of saving money.</p>

<h2>Conclusion</h2>

<p>I do somewhat regret how unscientific I&#39;ve been about implementing these new practices and hardware, because my doing them all at once will pretty much make it impossible to tell which ones work and which ones don&#39;t, because all of the changes in power usage will happen in the same month. My water bill should be easier to interpret, so at least I&#39;ll be able to report on just how useful shower shortening is.</p>

<p>The other bummer here is that I won&#39;t be able to report any results for at least a few weeks. Even then, my bills may be a little inconclusive since I made these changes about a third of the way into each billing cycle. Hopefully, if nothing else, I&#39;ll be able to report whether or not it&#39;s working in a few weeks, even if I can&#39;t very well quantify just how well it&#39;s working.</p>

<p>If anyone is curious about any of the things I&#39;ve talked about here, just let me know. I plan on writing a more substantial review of the Nest by itself after I&#39;ve had it for a little longer, so stay tuned for that if you&#39;re interested but hesitant to spend the money.</p>

<p>Cheers!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[The Pain of Prepaid Gift Cards]]></title>
<link href="http://jetheis.com/blog/2013/01/01/the-pain-of-prepaid-gift-cards/"/>
<updated>2013-01-01T15:00:00-05:00</updated>
<id>http://jetheis.com/blog/2013/01/01/the-pain-of-prepaid-gift-cards</id>
<content type="html"><![CDATA[<p>Over the last year or so, I&#39;ve been given a few prepaid gift cards as rewards and presents. The ones I&#39;ve received have all been Discover and American Express cards, but I imagine the problems I&#39;ve had are not just limited to these two types.</p>

<p>If you haven&#39;t seen them before, the cards look something like this (image from <a href="https://www.americanexpress.com/gift-cards/">American Express</a>):</p>

<p><img src="http://img.jetheis.com/d004e8afe02a46fe8a5ad677b102e2930a11a292.png" width="260" height="164" /></p>

<p>I do most of my shopping online, so I have often tried to use these cards on web stores (specifically Amazon), with very little success.</p>

<p>Because online stores require you to enter so much information (billing address, billing phone, name on card, etc.), it seems almost impossible to get the right combination of data that will let the card go through. Sometimes, the billing address needs to be that of the person who originally ordered the card; sometimes the name does too.</p>

<p>Amazon (and probably other stores) often authorize the card at a later time, so I&#39;ve ended up in this weird crisis situation where my order is being processed, but my payment hasn&#39;t gone through, and I get an alarming email from Amazon with a deadline, like this:</p>

<!-- more -->

<p>We&#39;re having trouble completing the payment for the above order. The issuing bank may have declined the charge if the name, expiration date, or ZIP Code you entered at Amazon.com does not exactly match the bank&#39;s information.</p>

<p>Valid payment information must be received within 3 days, otherwise your order will be canceled. </p>

<p>Now I&#39;ve got three days to try to figure this out (by trying random combinations of billing information), or I&#39;ll have to decide to just pay from my bank account. That sucks. So what do we do about it?</p>

<p>Most of the gift cards I&#39;ve been given have listed in their rules that they can&#39;t be used to buy other gift cards. From experience, though, that rule does not seem to be very widely enforced, so here&#39;s what an Amazon purchase from a prepaid card looks like for me now:</p>

<p>Walk into a brick-and-mortar grocery store (Kroger), buy an Amazon gift card with my prepaid card, then use the Amazon gift card to make my actual purchase.  Sometimes I&#39;ve gotten a weird look from the grocery store cashier when he or she has asked to see my ID and realized the name on the card was &quot;A Gift For You&quot;, but it&#39;s never prevented the purchase from going through.</p>

<p>For best results, it stands to reason that buying a gift card at the least technologically advanced store would have the highest chance of success, since they probably don&#39;t have a system that checks for gift card purchases. To find a suitable store near you, check out Amazon&#39;s <a href="http://www.amazon.com/gp/feature.html?ie=UTF8&amp;docId=1000465651">list of gift card retailers</a>, many of which are gas stations and grocery stores.</p>

<p>Hopefully this helps someone who&#39;s struggling with the same problem of being unable to actually use the money locked inside a prepaid card. At the very least, I&#39;d encourage anyone thinking about buying one of these cards to reconsider and maybe just buy a store-specific card.</p>

<p>Cheers, and Happy New Year!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[The Missing NPR Podcasts]]></title>
<link href="http://jetheis.com/blog/2012/12/19/the-missing-npr-podcasts/"/>
<updated>2012-12-19T20:52:00-05:00</updated>
<id>http://jetheis.com/blog/2012/12/19/the-missing-npr-podcasts</id>
<content type="html"><![CDATA[<p>It&#39;s been a little while since I&#39;ve posted, and hopefully it&#39;s been worth the wait, because I do have some sort of exciting news: <strong>I&#39;ve finished a personal project!</strong> (It really doesn&#39;t happen that often.)</p>

<p>What&#39;s the project, you ask? Well, let me tell you.</p>

<p>The two most popular <a href="http://www.npr.org/">NPR</a> programs, <a href="http://www.npr.org/programs/morning-edition/">Morning Edition</a> and <a href="http://www.npr.org/programs/all-things-considered/">All Things Considered</a>, don&#39;t have official podcasts on NPR&#39;s website. You can listen to the stories with their online player, and you can even get the content and audio of the stories through their <a href="http://www.npr.org/api/">API</a>, but there just isn&#39;t a podcast for either one of them.</p>

<p>That&#39;s where this project comes in.</p>

<!-- more -->

<p>First, though, a note. The main reason there isn&#39;t a podcast for either program is because NPR makes a lot of its money from licensing Morning Edition and All Things Considered to member stations, who in turn pay based on how many listeners the programs get.</p>

<p>This means that if you use this website (or listen only on NPR&#39;s site, or consume the API on some other device yourself), and <strong>don&#39;t</strong> listen to your local member station, you are essentially taking away their business. <strong>Please don&#39;t do that</strong>. I made this project because I really like NPR and I want others to be able to enjoy it too and see it be successful. If you truly are an online-only listener, or if you depend on NPR for your daily news, please remember to donate to <a href="http://www.npr.org/stations/">your local member station</a>.</p>

<p>Alright, back to the project.</p>

<p>It seems simple now compared to all the introduction and caveats above, but I suppose it is pretty simple: I made a website that allows you to subscribe to a Morning Edition or All Things Considered podcast. If you&#39;re interested, check it out (you&#39;ll find more complete instructions on the actual site):</p>

<p><strong><a href="http://www.missingnprpodcasts.com">http://www.missingnprpodcasts.com</a></strong></p>

<p>Let me know if you have any questions or problems using the site, and happy listening!</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Saying Goodbye to Kindle Touch Ads (How I Hacked My Kindle Touch)]]></title>
<link href="http://jetheis.com/blog/2012/09/12/saying-goodbye-to-kindle-touch-ads/"/>
<updated>2012-09-12T17:00:00-04:00</updated>
<id>http://jetheis.com/blog/2012/09/12/saying-goodbye-to-kindle-touch-ads</id>
<content type="html"><![CDATA[<p>This post is a promised follow-up to a <a href="/blog/2012/08/23/the-kindle-touch-and-invasive-advertising/">previous post</a>, and in it I will explain the steps I took with my Kindle Touch (4th Generation Kindle) and MacBook Pro to remove the screensaver (and banner) ads that now come standard with Kindles.</p>

<p>This isn&#39;t intended necessarily to be a guide for others, because I&#39;m no expert, have only done it once, and can&#39;t possibly account for any problems that may arise in someone else&#39;s attempt to do the same. However, I&#39;ve found posts like this to be really helpful in my own experimenting. All I aim to do here is to record the steps I took, in order, to accomplish a really specific goal: get rid of the ads. I&#39;ve embedded any files I used here, just in case they happen to be unavailable at a future date.  Hopefully this will serve as a second reference for anyone who gets stuck following an official guide, or who&#39;s unsure of where to start in all of this.</p>

<p>So that&#39;s what this post is. It&#39;s a long one, and I&#39;ll randomly be switching between narrating and instructing, and I&#39;m sorry for that, but it&#39;s just easier to write that way. This post specifically applies to Kindle Touch (4th Generation Kindle) with firmware version <strong>5.1.2</strong>. Anything after that I can make no promises for (heck, I can&#39;t make promises for this), but hopefully it&#39;s still in some way helpful. If you&#39;re interested, read on.</p>

<!-- more -->

<p>First, how I got started. I found seriously every bit of the information I needed to accomplish this ad removal on <a href="http://wiki.mobileread.com/wiki/Kindle_Touch_Hacking">this MobileRead page</a>, though I&#39;ve taken some of the steps that involve installing lots of packages replaced them with doing the same things the packages do by hand.  Please please please start there. It&#39;s very likely you&#39;ll also find everything you need on that page, and it&#39;s obviously kept much more up-to-date than this post ever will be. Before you do anything, though, make sure you read all you can. If you&#39;re using a MacBook (or a Linux machine, or anything really), you&#39;ll hopefully get some benefit reading this post before you start. I&#39;m not trying to scare you into reading my ramblings or anything, but I really would like to encourage you to get as much input as possible before you go doing things you can&#39;t undo. Also before using any files hosted here, make sure you check for a newer version on the wiki page.</p>

<h2>Step 1: The Jailbreak</h2>

<p>The actual jailbreaking of the Kindle is about the simplest thing in the world.  I just plugged the Kindle into my MacBook with the MicroUSB cable, putting it in USB Drive Mode, then copied <a href="http://dl.jetheis.com/kindle-jailbreak/data.stgz">data.stgz</a> into the root of the Kindle:</p>

<p><img src="http://img.jetheis.com/0ca886d1ad2bcc7b44805b0187074b4286ec1f90.png" width="779" height="452" /></p>

<p>After that, I ejected the Kindle, unplugged it from the computer, then selected <code>Menu</code> -&gt; <code>Settings</code> -&gt; <code>Menu</code> -&gt; <code>Restart</code>. While the Kindle was restarting, this new loading screen was visible:</p>

<p><img src="http://img.jetheis.com/e59c79aedc9686b26c1bb688ae5dfb2f60f697f7.png" width="400" height="533" /></p>

<p>After the Kindle was finished restarting, I saw the confirmation placeholder book, which I deleted (and you can too):</p>

<p><img src="http://img.jetheis.com/61461c81047554e4774551d26de2b3e564f07ae4.png" width="400" height="300" /></p>

<p>And... that&#39;s it. We&#39;re jailbroken.</p>

<h2>Step 2: Connecting</h2>

<p>Alright, now the Kindle&#39;s jailbroken. That&#39;s all well and good, but what does that actually get us? Well, jailbreaking simply installs another developer key that can be used to put custom apps on the Kindle. We&#39;re going to use it to install a package that enables USB Networking, a service that allows us to connect directly to the Kindle through a command line.</p>

<p>To do this, I copied
<a href="http://dl.jetheis.com/kindle-jailbreak/update_simple_usbnet_1.1_install.bin">update_simple_usbnet_1.1_install.bin</a>
into the root of the Kindle:</p>

<p><img src="http://img.jetheis.com/9fd417d6863f073d4298625ab3a300d1c47fc534.png" width="688" height="423" /></p>

<p>Then I ejected the Kindle, unplugged it, and installed the update by choosing <code>Menu</code> -&gt; <code>Settings</code> -&gt; <code>Menu</code> -&gt; <code>Update Your Kindle</code> from the Kindle.</p>

<p>Once the Kindle has finished updating, I set the password (needed for login) by entering the following in the search bar:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">;un password somepassword
</code></pre></div>
<p>Obviously using something besides <code>somepassword</code> for the password. Then I turned on USB Networking by entering the following in the search bar:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">;un
</code></pre></div>
<p>Now, before you do anything else, open up Terminal.app (under &quot;Utilities&quot; in the Applications folder), and enter the following command (without the <code>$</code>):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ ifconfig | egrep -o &quot;^[[:alnum:]]+&quot;
</code></pre></div>
<p>Leave the results of that command on the screen. Those are the networking devices your MacBook can currently see. Now connect the MacBook to the Kindle with the MicroUSB cable. It shouldn&#39;t go into USB Drive Mode like it usually does. If that&#39;s the case, we&#39;re in business. If not, eject the Kindle, enter <code>;un</code> again, and try again.</p>

<p>Now for a slightly complicated part. We need to figure out which device the MacBook thinks the Kindle is. Run the previous command again (by hitting the up arrow, then Enter), and compare the results. There should be one extra device listed (probably at the bottom). In my case, this device was <code>en3</code>.</p>

<p>Once you&#39;ve figured out the name of your Kindle (maybe it&#39;s the same one), run the following command to configure it (you&#39;ll be prompted for your computer&#39;s administrative password):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ sudo ifconfig en3 192.168.15.201
</code></pre></div>
<p>Obviously use something besides <code>en3</code> if you found your Kindle to have a different name, but make sure you use the same IP address. The Kindle relies on a really specific IP address belonging to it (<code>192.168.15.244</code>), so we configure it to have one somewhat close to that.</p>

<p>If all went well, we should be able to connect to the Kindle now. Enter the following command:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ ssh root@192.168.15.244
</code></pre></div>
<p>You&#39;ll be prompted for a password. This time, it&#39;ll be the one you set up in the search bar of the Kindle. If your login succeeds, you should see a notice and prompt like this:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">#################################################
#  N O T I C E  *  N O T I C E  *  N O T I C E  # 
#################################################
Rootfs is mounted read-only. Invoke mntroot rw to
switch back to a writable rootfs.
#################################################
[root@kindle root]# 
</code></pre></div>
<p>That&#39;s the command line prompt of the Kindle, and that means we&#39;re in.</p>

<h2>Step 3: Breaking The Ads</h2>

<p>Now that we&#39;ve got administrator command line access to the Kindle, all we need to do is disrupt the way the Kindle downloads and stores its ads enough that it will fail back to just displaying the standard screensavers. To do this, we&#39;ll first do exactly what the giant notice says, remount the root file system (just like the <code>$</code>s, don&#39;t type the <code>#</code>):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># mntroot rw
</code></pre></div>
<p>Now let&#39;s go find those ads. </p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># cd /var/local
# ls adunits
</code></pre></div>
<p>You should see something like this:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">1314512564245     4947350798237     7905915218628     8863644723128 9772216502999
2248931205866     5179523322443     8060227834200     8982827228459 admgr.json
3254245097874     5480504762067     8565874945799     9114782852577 admgr.seenactive
</code></pre></div>
<p>Those are all the sub-folders within the <code>adunits</code> folder, and that&#39;s where each ad&#39;s data is (for the most part), stored. What we&#39;re going to do, simply put, is turn off the main Java framework, rename the <code>adunits</code> folder (in case we ever want to restore it), replace it with just an empty file, remove all permissions to that file, then restart the main Java framework again. This way, <code>adunits</code> still exists if the framework checks for it (and all folders are really just special kinds of files), but the Kindle will fail to read or write any information to or from it, causing it to fail back to regular screensavers.  Anyway, here we go. Careful not to mess these up.</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># stop framework
# mv adunits adunits.bkp
# touch adunits
# chmod 000 adunits
# start framework
</code></pre></div>
<p>That&#39;s it; we&#39;re done. If you&#39;re interested in unjailbreaking your Kindle but leaving the ads turned off, skip down to the next section.</p>

<p>Otherwise, exit the shell with the <code>exit</code> command (you should see your prompt return to the one from your MacBook (ending in a <code>$</code>)), then restart your Kindle for posterity&#39;s sake. At this point, our mission is complete. No more ads.</p>

<h2>Step 4: Covering Our Tracks</h2>

<p>Now, even though we&#39;ve managed to disable the Kindle ads, our Kindle is in sort of a weird state. There&#39;s an extra developer key installed, a set of applications that allows direct connection and command line access, and the ad storage files and folders mixed around. We can actually undo all but the last one to put our Kindle back in a sort of a non-jailbroken but still ad-free state, and that&#39;s exactly what I did with mine (though I actually left the USB Networking stuff in just in case I wanted access later the first time).</p>

<p>These steps are entirely optional, but they do provide a little peace of mind in our knowing that our Kindle is <em>nearly</em> normal after its surgery.</p>

<h3>Removing USB Networking</h3>

<p>This is the step I didn&#39;t actually do at first, but then thought better of it.  To remove USB Networking, exit the Kindle shell with the <code>exit</code> command, unplug it, run the <code>;un</code> command in the search bar to turn off USB Networking, then plug it back in to enter USB Drive Mode. Copy <a href="http://dl.jetheis.com/kindle-jailbreak/update_simple_usbnet_1.1_uninstall.bin">update_simple_usbnet_1.1_uninstall.bin</a> into the root of the Kindle:</p>

<p><img src="http://img.jetheis.com/fc31c1b0a3e9ace4eaab70dcf5385da47811b7de.png" width="743" height="437" /></p>

<p>Eject it, unplug it, then from the Kindle select <code>Menu</code> -&gt; <code>Settings</code> -&gt; <code>Menu</code> -&gt; <code>Update Your Kindle</code>. If the update is successful, USB Networking has been uninstalled. To test it, no amount of entering <code>;un</code> at the search bar should be able to prevent the Kindle from going into USB Drive Mode when plugged in.</p>

<h3>Unjailbreaking</h3>

<p>To unjailbreak the Kindle, we just need to delete the new developer key. We can do this by hand (if you haven&#39;t logged out of your Kindle shell yet and uninstalled USB Networking), or by running the uninstaller. To do it by hand, just run the following commands:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># cd /etc/uks
# ls
</code></pre></div>
<p>You should see the following:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">pubdevkey01.pem   pubprodkey01.pem  pubprodkey02.pem
</code></pre></div>
<p>The first one is the new developer key. Just delete it:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text"># rm -f pubdevkey01.pem
# ls
</code></pre></div>
<p>You should now see it missing from the list.</p>

<p>If you&#39;d rather (or must because you&#39;ve removed USB Networking) do it with the official uninstaller (that does the same thing, just with a fancy status bar), exit the shell, turn off USB Networking with <code>;un</code> in the search bar, then plug the Kindle back in so it goes into <code>USB Drive Mode</code>. Copy <a href="http://dl.jetheis.com/kindle-jailbreak/update_jailbreak_1.2.N_uninstall.bin">update_jailbreak_1.2.N_uninstall.bin</a> into the root of the Kindle:</p>

<p><img src="http://img.jetheis.com/7bd11cfc19f3005a6f7a18e4d127a9c09d536caf.png" width="779" height="452" /></p>

<p>Eject it, then from the Kindle select <code>Menu</code> -&gt; <code>Settings</code> -&gt; <code>Menu</code> -&gt; <code>Update Your Kindle</code>. If the update was successful, your Kindle is unjailbroken.</p>

<p>I&#39;m quite happy with my ad-free, unjailbroken, inaccessible-through-command-line-interface Kindle Touch. I hope this post was helpful to you; I enjoyed writing it.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[The Kindle Touch and Invasive Advertising (a Rant)]]></title>
<link href="http://jetheis.com/blog/2012/08/23/the-kindle-touch-and-invasive-advertising/"/>
<updated>2012-08-23T08:00:00-04:00</updated>
<id>http://jetheis.com/blog/2012/08/23/the-kindle-touch-and-invasive-advertising</id>
<content type="html"><![CDATA[<p>For our two year anniversary, Emilie got me a <a href="http://www.amazon.com/gp/product/B005890G8Y/">Kindle Touch</a> (a pretty sweet gift). When I first turned it on and started messing around with it, I really liked the fact that because of the <a href="http://en.wikipedia.org/wiki/E_Ink">E Ink</a> screen, which seems to hold its image without having to be constantly powered, the Kindle Touch never actually turns off its screen when put to sleep.  Rather, it puts a &quot;screensaver&quot; image up that really just serves to hide whatever you were working on or reading. Furthermore, the images it uses for its screensaver are <strong>gorgeous</strong>, really making the device look awesome sitting out in a public place. Here&#39;s a picture of my favorite one, and you can actually grab the whole set in <a href="/blog/2012/08/23/kindle-touch-backgrounds/">this post</a>.</p>

<p><img src="http://img.jetheis.com/48e3d2616c3ec279a5f57984fc09b3bddd8953bf.png" width="600" height="450" /></p>

<p>I kept happily exploring my cool new toy, including connecting it to the wifi in Emilie&#39;s apartment. I noticed that after connecting, banner ads started appearing at the bottom of the home screen (mostly advertising other Kindle books, but also including a few annoying ones from Intel (I think?) and AT&amp;T).</p>

<p>I wasn&#39;t really bothered by this, and a little bit of reading in the included manual told me that I actually owned the &quot;Special Offers&quot; edition of the Kindle Touch, which gives buyers about a $40 price break in return for having ads appear on the menu screens.</p>

<p>To me, this is no big deal (actually, I sort of like the idea, especially since the ads seem pretty relevant a lot of the time).  However, I got a lot less cheerful when I put the Kindle to sleep again and was presented with a whole new set of background, including these (actual images extracted from the device):</p>

<!-- more -->

<p><img src="http://img.jetheis.com/1f68944292a197483894489a2a6521abb497b1f8.png" width="300" height="400" />
<img src="http://img.jetheis.com/a0c2bcf6bdb987461ac50ee740eb7fa55498713c.png" width="300" height="400" /></p>

<p>Suddenly this &quot;Special Offers&quot; thing seems a lot less tolerable. In fact, this is enough to make me want to leave my Kindle in a drawer when people come over instead of out on a table, just because these ads are so unsightly.</p>

<h2>So What&#39;s The Big Deal?</h2>

<p>If you&#39;ve opened a web browser in the past few years, you&#39;ve come to realize that internet advertising has very seriously become part of our lives. Think about it: when&#39;s the last time you saw a &quot;punch the monkey for an iPod&quot; or &quot;you&#39;re the 1,000,000th visitor!&quot;? Probably not for a while. Instead, you see ads that are relevant to either your (perceived) interests or to the content you&#39;re reading. This is largely because advertisers (led in no small part by Google and its AdSense) have formed a much more mature, symbiotic relationship with users than ever existed in years past. We use free services, knowing they&#39;re being paid for by ads, but we don&#39;t mind because the ads aren&#39;t intrusive or offensive most of the time, and sometimes they&#39;re halfway relevant.</p>

<p>Now let&#39;s evaluate the case of Amazon and its Kindle &quot;Special Offers&quot; program.  First, despite any original good intentions or origins for the pricing model, as it stands now, the &quot;Special Offers&quot; version of the Kindle Touch is the only one displayed on the Amazon web site, and the product page has this to say about it:</p>

<p><img src="http://img.jetheis.com/2ee62a36cb04d584f72fc73a9a43b246c37135bd.png" width="671" height="389" /></p>

<p>What I don&#39;t gather from reading this is that whenever I put my Kindle down, I&#39;m going to see a giant, tasteless ad for AT&amp;T, not some pretty wood finish with a note at the bottom about a product. (I actually found myself cycling in and out of the screensaver until I at least got to an advertisement for a book before I put it down.)</p>

<p>What I also don&#39;t gather is that this is a price-subsidized model. Nowhere on this page am I told that I&#39;m actually being paid $40 to replace the gorgeous screensavers on my Kindle with ads.</p>

<p>What I <strong>really</strong> don&#39;t gather, and what I&#39;ve really had to wrap my head around for the past few days, is that because this screen never turns off, I&#39;m actually being paid $40 to <strong>install a permanent billboard</strong> in my apartment whenever I&#39;m not actively reading. Consider that. What if a cell phone provider put banner ads in every text you sent, or if your car played audio commercials from external speakers whenever it was parked.  I wouldn&#39;t accept $200 to have guests to my apartment force-fed advertising from companies I pretty decidedly dislike (that&#39;s the other thing: there&#39;s no feedback mechanism; you can&#39;t make ads more relevant, express your interests, or veto content. It&#39;s AT&T; it&#39;s MegaCorp; it&#39;s not an option.).</p>

<p>After purchasing the Kindle and enjoying its pretty screensavers, then having them suddenly get replaced by unsightly ads, users can find a settings page on Amazon&#39;s website that will allow them to pay an additional $40 to remove the plague of ads they hardly realized the signed up for. This is pretty unacceptable.</p>

<h2>What Do We Do About It?</h2>

<p>I&#39;m not a huge fan of &quot;hacking&quot; or &quot;jailbreaking.&quot; I&#39;ve been down that road of tinkering with electronics, and while it&#39;s fun in the short term, it almost always causes problems in the long term that make you really just wish you could have things back to the way they were. As a result, I&#39;m really only game for &quot;jailbreaking&quot; if it solves a really significant problem. In my opinion, this is the case with the Kindle.</p>

<p>The Kindle Touch is a <strong>wonderful</strong> piece of hardware, and its software is pretty top-notch, too, especially for the function it serves, but the permanent ads are just too much.</p>

<p>Generally speaking (I am not a lawyer), there&#39;s a precedent for jailbreaking not being illegal, even if it is in violation of a terms of use contract for a device or service (especially for a Linux-based device like the Kindle). What it can cause is a voiding of one&#39;s warranty or a discontinuing of one&#39;s provided services.</p>

<p>For this reason, and because I&#39;m really not a fan of this $40 shakedown from Amazon if my dissatisfaction with ads grows unbearable, I consider the Kindle Touch not to have a $40 ad removal fee, but to have a $40 optional warranty. I have no intention of paying that $40, and if that voids my warranty, so be it.  I&#39;ve jailbroken my Kindle Touch, taken a couple of steps to disable the ads, then left it alone, and I couldn&#39;t be happier with the results. I&#39;ve got my beautiful screensavers back, but other than that the device functions as it always has. I&#39;ll cover my process of doing this in a later post for anyone interested.</p>

<p>So to wrap it up, here&#39;s where I stand: advertisers are welcome to bring relevant, non-intrusive ads into my life, and I won&#39;t block them. Heck, I&#39;ll probably click on a few of them. However, there is a line, and once it&#39;s crossed by, say, attempting to commandeer my coffee table as a billboard, the symbiotic relationship is broken. Plenty of people will do a little research, figure out how to turn the badness off, and resume their lives, even at the expense of a warranty here or there. By being too aggressive, advertisers and advertising platforms lose, and I hope the folks at Amazon look long, hard at this pricing model before it gets any worse.</p>

<p>As for me, I&#39;m going to pretend this all never happened, that there never was a &quot;Special Offers&quot; edition of the Kindle Touch. Now, if you&#39;ll excuse me, I&#39;ve got a book to read.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Kindle Touch Backgrounds]]></title>
<link href="http://jetheis.com/blog/2012/08/23/kindle-touch-backgrounds/"/>
<updated>2012-08-23T07:55:00-04:00</updated>
<id>http://jetheis.com/blog/2012/08/23/kindle-touch-backgrounds</id>
<content type="html"><![CDATA[<p>This post is actually just a part of <a href="/blog/2012/08/23/the-kindle-touch-and-invasive-advertising/">another post</a>, but I thought I&#39;d best break it out into another post for search engines&#39; sake.</p>

<p>I am obviously not a lawyer. I do not own any of these images; they are the property of Amazon.com Inc. and distributed alongside the <a href="http://www.amazon.com/gp/product/B005890G8Y/">Kindle Touch</a> device (which you really should buy one of).</p>

<p>Understand that these images are copyright property of Amazon.com Inc. and that you may only use them with permission, not from me, but from the copyright owners.</p>

<p>With that said, and reminding you again to follow all the rules, enjoy!</p>

<!-- more -->

<p><a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss00.png" ><img src="http://img.jetheis.com/46dff7fcbf0f6d132125423eb52becd4845bb1d2.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss01.png" ><img src="http://img.jetheis.com/9cbfe7889aefd189959c008015c2e1dd246f821a.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss02.png" ><img src="http://img.jetheis.com/5b7be7938229ab69e6b0f6ba5577fe79b0a41c68.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss03.png" ><img src="http://img.jetheis.com/be9671040b08050ca6207fa9c8ecc8941362861a.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss04.png" ><img src="http://img.jetheis.com/eba4e1fe6c755809ec991beea2ce57190e2a55f3.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss05.png" ><img src="http://img.jetheis.com/f7524d5d59c5e02de49e99e2369495a479c3051b.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss06.png" ><img src="http://img.jetheis.com/683a992193c7d499b8ad0ebdac23df4fe2de86f5.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss07.png" ><img src="http://img.jetheis.com/040e8d4c549dc0061ac43c41f5c98ba41ccd86a5.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss08.png" ><img src="http://img.jetheis.com/9ba1b83ebac9d851cf788ff0350105fca20f74fd.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss09.png" ><img src="http://img.jetheis.com/e7b559366833e537b8816ad694fe0914755a120c.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss10.png" ><img src="http://img.jetheis.com/08bc4efce55e425de29db7abf458367dfacb903e.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss11.png" ><img src="http://img.jetheis.com/a9b1ac5d54b5ca35fac351f2da9a2fdb359bcf37.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss12.png" ><img src="http://img.jetheis.com/32c240c090bdabf21ee80bf9eb236fa1dc1a80b6.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss13.png" ><img src="http://img.jetheis.com/68327dd1a5177f89df8583e70c51546665fa7d97.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss14.png" ><img src="http://img.jetheis.com/e4eaf3f73e74e912d89fb718da37b6ba81b73edf.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss15.png" ><img src="http://img.jetheis.com/12404b91a2c48fa2125ce2e1219da31fd1aa7cb1.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss16.png" ><img src="http://img.jetheis.com/5bec8a1e9de82e4180f19c25afb14cd3ea00ee1b.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss17.png" ><img src="http://img.jetheis.com/58f3db903dcdf07ec410179b849d1b3c04b9df90.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss18.png" ><img src="http://img.jetheis.com/7d15567358b969fc65244ecd573cee8c1c0f8464.png" width="150" height="200" /></a>
<a href="http://dl.jetheis.com/kindle-backgrounds/bg_xsmall_ss19.png" ><img src="http://img.jetheis.com/d076c74f7efdb94f9284000a9c1530defdd41be1.png" width="150" height="200" /></a></p>

<p>You can also download the complete set as a ZIP archive <a href="http://dl.jetheis.com/kindle-backgrounds/kindle-backgrounds.zip">here</a>.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Cutting S3 Costs with Metadata]]></title>
<link href="http://jetheis.com/blog/2012/03/08/cutting-s3-costs-with-metadata/"/>
<updated>2012-03-08T11:04:00-05:00</updated>
<id>http://jetheis.com/blog/2012/03/08/cutting-s3-costs-with-metadata</id>
<content type="html"><![CDATA[<p>Having <a href="http://pages.github.com/">GitHub Pages</a> provide free hosting for this static blog is a huge blessing, but it obviously does come with some limitations. Specifically, a free GitHub account only gives you 0.30 GB (300 MB) of storage space. Even with a Micro Account (free through <a href="https://github.com/edu">GitHub educational accounts</a>), there&#39;s a limit of 0.60 GB (600 MB). This is obviously plenty of space for source code, pages, etc., but is simply not suited for hosting media like videos, slide decks, or large images.</p>

<p>As a result, I, like many others, have turned to <a href="https://aws.amazon.com/s3/">Amazon S3</a>, a service actually geared toward hosting media and other non-text content. With S3&#39;s <a href="https://aws.amazon.com/free/">Free Usage Tier</a>, I actually haven&#39;t had to pay a dime yet (it lasts for one year), and when I do, it&#39;ll be a very small amount.</p>

<p>S3 uses a pay-for-what-you-use style of billing, where you&#39;re billed based on how much storage you&#39;re using, how many requests are made for files you&#39;ve uploaded, and how much data actually gets transferred. It&#39;s changed a few times, so I won&#39;t bother copying down the billing plans, as they&#39;ll likely be outdated quickly. Instead, I&#39;ll just <a href="https://aws.amazon.com/s3/pricing/">link to them</a> for anyone interested.</p>

<p>Because S3 bills based on how much data gets requested per month, anything we can do to reduce that number actually saves money. Is there something we can do?  Well, yes.</p>

<!-- more -->

<h2>Making Our Files Smarter</h2>

<p>It&#39;s sort of a perfect solution: reduce the number of requests coming into our S3 account <strong>and</strong> speed up page load times, without the user being negatively affected at all. All this is accomplished by setting the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21">Expires</a> value of our files to some date off in the future. This will become the HTTP <code>Expires</code> header value that lets browsers know not to request the resources over and over again, instead loading them (nearly instantly) from their local caches.</p>

<p><strong>NOTE</strong>: This is obviously only useful for resources that actually won&#39;t change, like images.</p>

<p>We can set this metadata value from the <a href="https://console.aws.amazon.com/s3/home">S3 Console</a> either at upload time (<code>Set Details</code> -&gt; <code>Set Permissions</code> -&gt; <code>Set Metadata</code> in the upload wizard), or from the <code>Properties</code> pane that can be opened for any existing file (find it under the <code>Metadata</code> tab).</p>

<p>In either case, use the <code>Add more metadata</code> button to create a new key/value pair with <code>Expires</code> for the key and the file&#39;s expiration date for the value:</p>

<p>(during initial upload):</p>

<p><img src="http://img.jetheis.com/2f684ad655e175f9ef26197384fc8cc0d972bc9b.png" width="472" height="138" /></p>

<p>(or after initial upload):</p>

<p><img src="http://img.jetheis.com/93c8db6f1b6ee8f89a50b8478847dd68750d16ee.png" width="489" height="204" /></p>

<p>Now, here&#39;s a possible sticky point. HTTP specifies that <code>Expires</code> dates should be in <a href="https://www.ietf.org/rfc/rfc1123.txt">RFC 1123</a> format. It&#39;s a very exact format that can be easily messed up if you&#39;re trying to enter it by hand. Luckily, Ruby&#39;s <a href="http://www.ruby-doc.org/stdlib-1.9.3/libdoc/cgi/rdoc/CGI.html">CGI class</a> has a built-in way of generating these dates. If you&#39;re using a Mac (or a Linux/BSD box with Ruby installed), use this handy one-liner in a terminal (<code>Terminal</code> app on Mac under <code>Utilities</code> in the <code>Applications</code> folder) to generate the date (without the <code>$</code>):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ ruby -r CGI -e &#39;puts CGI.rfc1123_date(Time.local(2013, 7, 14))&#39;
</code></pre></div>
<p>Instead of <code>2013, 7, 14</code> (my 24th birthday), use whichever date you have in mind. The format is <code>year, month, day</code>. If you&#39;re really fancy (and sure that your files won&#39;t change), use the max value for a 32-bit timestamp:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ ruby -r CGI -e &#39;puts CGI.rfc1123_date(Time.at(2 ** 31 - 1))&#39;
</code></pre></div>
<p>Here&#39;s that value for any impatient copy-and-pasters:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">Tue, 19 Jan 2038 03:14:07 GMT
</code></pre></div>
<p>Now that we know how to set our file expiration date, let&#39;s look at what actually happens when we do.</p>

<h2>Does It Actually Work?</h2>

<p>Here&#39;s an S3-hosted image of <a href="http://octodex.github.com/octobiwan/">Octobi Wan Catnobi</a> that has no extra metadata set:</p>

<p><img src="http://dl.jetheis.com/s3-metadata/octobiwan.jpg" width="300" height="300" /></p>

<p>and here&#39;s an S3-hosted image of <a href="http://octodex.github.com/spocktocat/">Spocktocat</a> that has its <code>Expires</code> header set:</p>

<p><img src="http://dl.jetheis.com/s3-metadata/spocktocat.jpg" width="300" height="300" /></p>

<p>We can verify that this is true pretty easily. If you&#39;re using a UNIX-like operating system (basically anything but Windows), open a terminal (<code>Terminal</code> application on a Mac, found under <code>Utilities</code> in the <code>Applications</code> folder), and enter the following command (without the <code>$</code>) to make a request for the first image:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ curl -v http://dl.jetheis.com/s3-metadata/octobiwan.jpg &gt; /dev/null
</code></pre></div>
<p>Now look at the portion of the output that is prefixed with a <code>&lt;</code>. Those are all of the response headers:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">&lt; HTTP/1.1 200 OK
&lt; x-amz-id-2: +JWpez+WdCrliLIuJgb1JQ9GrdUaM2QkRF/Cc+oZBmvtZWMBCsYb4bavdptvXJEo
&lt; x-amz-request-id: 616907A681625FE3
&lt; Date: Wed, 07 Mar 2012 17:05:14 GMT
&lt; Last-Modified: Wed, 07 Mar 2012 16:38:05 GMT
&lt; ETag: &quot;dcef3abedf0e0761203aaeb85886a6f3&quot;
&lt; Accept-Ranges: bytes
&lt; Content-Type: image/jpeg
&lt; Content-Length: 65307
&lt; Server: AmazonS3
</code></pre></div>
<p>No <code>Expires</code>. Let&#39;s try the image we set that header on:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ curl -v http://dl.jetheis.com/s3-metadata/spocktocat.jpg &gt; /dev/null
</code></pre></div>
<p>We&#39;ll look at the output prefixed with <code>&lt;</code> again:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">&lt; HTTP/1.1 200 OK
&lt; x-amz-id-2: IZevTGh6eOzexdotN5RaSTaROUtmWc+7hWpq1t7CVD6Mz98G2m174S35wFEkk8pD
&lt; x-amz-request-id: E321C35ACC2CB79E
&lt; Date: Wed, 07 Mar 2012 17:10:07 GMT
&lt; Expires: Sun, 14 Jul 2013 04:00:00 GMT
&lt; Last-Modified: Wed, 07 Mar 2012 16:44:52 GMT
&lt; ETag: &quot;c817148a2d2589ca977fed81c2e5a6f2&quot;
&lt; Accept-Ranges: bytes
&lt; Content-Type: image/jpeg
&lt; Content-Length: 69947
&lt; Server: AmazonS3
</code></pre></div>
<p>And there it is: our shiny new <code>Expires</code> header:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">Expires: Sun, 14 Jul 2013 04:00:00 GMT
</code></pre></div>
<h2>A Better Example</h2>

<p>While this is all well and good, we haven&#39;t quite proven that setting this header does anything or cuts any costs. We&#39;ll now conduct a true test of the new configuration using Google Chrome (this one can be performed on Windows, too).  Follow along if you&#39;d like.</p>

<p>First, browse to this page using Chrome and open the Developer Tools pane (Alt + Command + I on a Mac, or by clicking the wrench and selecting <code>Tools</code> -&gt; <code>Developer Tools</code>).</p>

<p><img src="http://img.jetheis.com/b8727960d23b9b5d82dd1c56c4a28184c2203d00.png" width="585" height="368" /></p>

<p>In the Developer Tools pane, click the <code>Network</code> tab. We&#39;ll see that there isn&#39;t anything there yet, because Chrome isn&#39;t &quot;recording&quot; network activity unless the pane&#39;s open.</p>

<p><img src="http://img.jetheis.com/0367d0c02f149d0be6146e9a6495853c8bc84cec.png" width="836" height="374" /></p>

<p>Now click the <a href="/">Jimmy Theis</a> at the top of the blog to jump back to the home page. We&#39;re navigating away from and back to the page because pushing <code>Refresh</code> actually forces all resources to be reloaded.</p>

<p>Now here&#39;s the kind of wonky part. Chrome does a great job of making browsing fast, so it actually does cache resources without expiration dates for a short time. To circumvent this, it seems the resources need to not be accessed for quite a while. So, forget about this and go browse Reddit or Hacker News or something and come back in about 30 minutes.</p>

<p>Once you&#39;ve browsed some and allowed sufficient time, find the link to this post on the homepage (or the archives if this is old enough), and click on it. Make sure the Developer Tools pane is still open.</p>

<p>Once the page has loaded, scroll through the network activity. You should see both <code>octobiwan.jpg</code> and <code>spocktocat.jpg</code> in the list, probably somewhat close to each other.</p>

<p><img src="http://img.jetheis.com/4f3b3022071167c4bd52cf8d903783b7c717ad70.png" width="761" height="89" /></p>

<p>Whoa! Something actually happened. We can see that <code>spocktocat.jpg</code> was loaded &quot;(from cache)&quot; and took a total of 0 ms to load, while <code>octobiwan.jpg</code> shows a 304 &quot;Not Modified&quot; response, a data transfer of 286 bytes, and a latency of 206 ms. So what does this mean?</p>

<p>Let&#39;s look at our base case first: <code>octobiwan.jpg</code>, which has no information dictating when it expires, could have potentially changed (from the browser&#39;s point of view), so Chrome makes a new request for the image. The intelligent computer at Amazon gave it back a 304 response, which simply tells the browser that the resource hasn&#39;t changed, so we won&#39;t bother sending it again. This saves us the bandwidth of downloading the entire image again (the request is 286 bytes instead of the full 60+ KB the image takes up), but it still makes a request to S3, and that costs money.</p>

<p>On the other hand, <code>spocktocat.jpg</code> is marked as not expiring for years from now, so Chrome doesn&#39;t even bother making a request at all. It simply grabs the image from the cache and displays it. This speeds up page load and saves us money on our Amazon bill. As a side note, if that image where to ever change, doing a hard refresh of the page would force it to be reloaded, so we&#39;re not stuck with an outdated image if we ever change our minds.</p>

<h2>Conclusion</h2>

<p>It&#39;s relatively simple to set the <code>Expires</code> value of content hosted on S3, and it does have a real effect on both site performance and the number of requests made to Amazon. In fact, apart from the work of setting those values, there isn&#39;t much of a downside to the practice. Some software suites offer this functionality in a little more accessible interface, and lots of other posts recommend using these (paid) software suites to set these extra metadata values.  The purpose of this post, however, is really to highlight a potential problem and present a solution that doesn&#39;t require spending extra money. Feel free to try any S3 management software, but know that you&#39;ve already got the tools you need to start tuning your account for cost effectiveness.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Solving One Tough Puzzle]]></title>
<link href="http://jetheis.com/blog/2011/12/31/solving-one-tough-puzzle/"/>
<updated>2011-12-31T13:00:00-05:00</updated>
<id>http://jetheis.com/blog/2011/12/31/solving-one-tough-puzzle</id>
<content type="html"><![CDATA[<p>This year for Christmas, Emilie&#39;s grandparents got each family member a different puzzle or game. One in particular that really caught my eye was Emilie&#39;s sister&#39;s gift: a three-by-three puzzle that claims to have &quot;300,000 wrong ways, but only one right way to assemble.&quot;</p>

<p>The puzzle is called &quot;One Tough Puzzle&quot; (available from <a href="http://www.amazon.com/Great-American-Puzzle-Factory-120/dp/B000A32O2E">Amazon</a> and others) and consists of 9 puzzle pieces, each with four interlocks (two in and two out). The interlocks are shaped like each of the four suits in a conventional deck of cards, so a positive heart interlock can link with a negative heart interlock, and so on. Below is a picture of what the puzzle pieces more or less look like.</p>

<p><img src="http://img.jetheis.com/bacd71c9bfeded19f884441e2e5c7237ba8b06f9.png" width="300" height="184" /></p>

<p>Image credit: <a href="http://www.greatamericanpuzzle.com/puzzleDetails.php?productId=5149&amp;categoryId=24">Official Product Page</a></p>

<p>To solve the puzzle, one must arrange the pieces such that they form a three by three square when interlocked. The outer edges don&#39;t necessarily have to be neat or uniform in any way, but the box does specify that there will be six interlocks protruding from the edge of the finished puzzle. Because of this lack of an edge, the puzzle offers basically no hints as to whether or not you&#39;re headed down the right path, making it especially frustrating and probably near impossible for someone without tons of time on his or her hands.</p>

<p>After a lot of trying, I did solve it (though not necessarily by conventional means).  So, for the impatient (or the Google searchers), here&#39;s the solution (assuming your puzzle pieces match the ones I worked with):</p>

<!-- more -->
<div class="highlight"><pre><code class="text language-text" data-lang="text">H: Heart
C: Club
S: Spade
D: Diamond
             D       S     
 +---|---+---|---+---|---+ 
 |   S   |       |       | 
 -H      -D      -H      -S
 |       |   H   |   C   | 
 +---|---+---|---+---|---+ 
 |   S   |       |       | 
 -C      -S      -C      -D
 |       |   H   |   D   | 
 +---|---+---|---+---|---+ 
 |   H   |       |       | 
 -D      -D      -C      -H
 |       |   C   |   C   | 
 +---|---+---|---+---|---+
</code></pre></div>
<p>For the more curious, I&#39;ll talk a little bit about how I solved it. If you&#39;re only here for the solution, bail out now.</p>

<p>First of all, I should make it clear that I didn&#39;t just try different combinations by hand until I found the solution. I&#39;m not nearly patient enough, and I don&#39;t think that would even be all that much fun. Instead, I wrote a small program (a script really) that tries all the possible arrangements until it finds a solution. This is obviously much faster than trying combinations by hand because no physical pieces are being moved, and a computer can think considerably faster than a human.</p>

<p>I wrote the script in JavaScript, the language used to add functionality to most web pages, so that anyone will be able to run it right here from this blog post. To that end, I&#39;ve created a jsFiddle that you can test drive right here. I apologize about how primitive the interface is; it&#39;s really meant to be a proof of concept. If you&#39;d like to create a better interface, feel free.</p>

<p>To use it, just set the interlocks on each of the pseudo pieces (the red squares at the top). <code>+</code> means the interlock is protruding, and <code>-</code> means it isn&#39;t. If your puzzle pieces are the same as mine, you shouldn&#39;t have to change a thing; just hit the <code>Solve</code> button. The solution to whatever you&#39;ve entered will print below the <code>Solve</code> button, unless one can&#39;t be found.</p>

<iframe width="100%" height="700" src="http://jsfiddle.net/jetheis/xe8Hz/4/embedded/result" allowfullscreen="allowfullscreen" frameborder="0"></iframe>

<p><strong>EDIT</strong>: I&#39;ve also added a second button, <code>Find Solutions with Flipped Pieces</code>, that will do exactly what it says. Any piece marked with an <code>F</code> in the solutions is a flipped version of another piece. (This one takes a few seconds to run)</p>

<p><strong>Mobile Users</strong>: Use <a href="http://jsfiddle.net/jetheis/xe8Hz/embedded/result/">this link</a> for full screen.</p>

<p>All of the code is available in a <a href="https://gist.github.com/1544631">Gist</a> (MIT License). If you&#39;d like to tinker with it, you should probably do it from the Gist, not the jsFiddle. I&#39;ve directly imported the solving functionality in the fiddle, so you can&#39;t really mess with it too much, but I have included an html page that has the same content as the fiddle in the Gist.</p>

<p>To play with it, clone the Gist, run the tests, and open the demo:</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">$ git clone git://gist.github.com/1544631.git onetoughpuzzle
$ cd onetoughpuzzle
$ open tests.html
$ open playground.html
</code></pre></div>
<p>If you&#39;re curious about the algorithm used, I&#39;ll talk about that now. At the core of the script is a function called <code>solvePuzzle</code> that does all the state exploration and real solving. It employs a <a href="http://en.wikipedia.org/wiki/Depth-first_search">depth-first search</a> using a <a href="http://en.wikipedia.org/wiki/Stack_(abstract_data_type)">stack</a> to keep track of states to be explored. By state, I mean the following:</p>

<ul>
<li>A partially filled board (filled left to right, top to bottom) with valid pieces placed in specific orientations.</li>
<li>The remaining pieces not yet used on the board.</li>
</ul>

<p>The algorithm, in plain English, looks like this:</p>

<ul>
<li>Create a new state that is just the empty board with all of the pieces remaining to be placed.</li>
<li>Add it to the workspace (states to be explored next, represented by a stack).</li>
<li>Repeat the following until a solution is found:

<ul>
<li>Pop (remove) the first state in the workspace.</li>
<li>Using this state, evaluate each piece remaining to be placed one at a time:</li>
<li>If the piece can fit in the next available space on the board in any of its four rotated orientations, create a new state with that piece placed in each valid orientation and add that to the workspace.</li>
</ul></li>
</ul>

<p>If a state is ever removed from the workspace and found to be a valid solution, the algorithm exits successfully. If, however, the workspace is ever depleted of states, no solution exists for the puzzle and the algorithm exits unsuccessfully. The actual code for the algorithm is below.</p>

<div class="highlight"><pre><code class="javascript"><span class="kd">var</span> <span class="nx">solvePuzzle</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">pieces</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">// Helper function that removes the first element of an array, if there is one.</span>
    <span class="kd">var</span> <span class="nx">shortened</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">ary</span><span class="p">)</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">ary</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">1</span> <span class="o">?</span> <span class="p">[]</span> <span class="o">:</span> <span class="nx">ary</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span> <span class="p">}</span>

    <span class="kd">var</span> <span class="nx">board</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Board</span><span class="p">();</span>
    <span class="kd">var</span> <span class="nx">workspace</span> <span class="o">=</span> <span class="p">[];</span>

    <span class="c1">// Push initial empty board state</span>
    <span class="nx">workspace</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">State</span><span class="p">(</span><span class="nx">board</span><span class="p">,</span> <span class="nx">pieces</span><span class="p">));</span>

    <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="p">{</span>

        <span class="c1">// Base case that will be triggered if a solution is not found</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">workspace</span><span class="p">.</span><span class="nx">length</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="p">)</span> <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>

        <span class="c1">// Retrieve the top state on the workspace</span>
        <span class="kd">var</span> <span class="nx">state</span> <span class="o">=</span> <span class="nx">workspace</span><span class="p">.</span><span class="nx">pop</span><span class="p">();</span>

        <span class="kd">var</span> <span class="nx">board</span> <span class="o">=</span> <span class="nx">state</span><span class="p">.</span><span class="nx">board</span><span class="p">;</span>
        <span class="kd">var</span> <span class="nx">pieces</span> <span class="o">=</span> <span class="nx">state</span><span class="p">.</span><span class="nx">pieces</span><span class="p">;</span>

        <span class="c1">// Base case that will be triggered when the board is filled</span>
        <span class="k">if</span> <span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">isFull</span><span class="p">())</span> <span class="k">return</span> <span class="nx">board</span><span class="p">;</span>

        <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">pieces</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
            <span class="c1">// Retrieve one of the remaining pieces</span>
            <span class="kd">var</span> <span class="nx">piece</span> <span class="o">=</span> <span class="nx">pieces</span><span class="p">[</span><span class="nx">i</span><span class="p">];</span>

            <span class="c1">// Strip that piece out of the pices left over</span>
            <span class="kd">var</span> <span class="nx">nextPieces</span> <span class="o">=</span> <span class="nx">pieces</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nx">i</span><span class="p">).</span><span class="nx">concat</span><span class="p">(</span><span class="nx">pieces</span><span class="p">.</span><span class="nx">slice</span><span class="p">(</span><span class="nx">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">pieces</span><span class="p">.</span><span class="nx">length</span><span class="p">));</span>

            <span class="c1">// Generate rotations of the retrieved piece</span>
            <span class="kd">var</span> <span class="nx">piece2</span> <span class="o">=</span> <span class="nx">piece</span><span class="p">.</span><span class="nx">rotated</span><span class="p">();</span>
            <span class="kd">var</span> <span class="nx">piece3</span> <span class="o">=</span> <span class="nx">piece2</span><span class="p">.</span><span class="nx">rotated</span><span class="p">();</span>
            <span class="kd">var</span> <span class="nx">piece4</span> <span class="o">=</span> <span class="nx">piece3</span><span class="p">.</span><span class="nx">rotated</span><span class="p">();</span>

            <span class="c1">// If any rotation of the piece fits in the next available space,</span>
            <span class="c1">// create the state with that piece added to the board and push</span>
            <span class="c1">// it onto the workspace stack</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">canFitNextPiece</span><span class="p">(</span><span class="nx">piece</span><span class="p">))</span> <span class="nx">workspace</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">State</span><span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">withNextPiece</span><span class="p">(</span><span class="nx">piece</span><span class="p">),</span> <span class="nx">nextPieces</span><span class="p">))</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">canFitNextPiece</span><span class="p">(</span><span class="nx">piece2</span><span class="p">))</span> <span class="nx">workspace</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">State</span><span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">withNextPiece</span><span class="p">(</span><span class="nx">piece2</span><span class="p">),</span> <span class="nx">nextPieces</span><span class="p">));</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">canFitNextPiece</span><span class="p">(</span><span class="nx">piece3</span><span class="p">))</span> <span class="nx">workspace</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">State</span><span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">withNextPiece</span><span class="p">(</span><span class="nx">piece3</span><span class="p">),</span> <span class="nx">nextPieces</span><span class="p">));</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">canFitNextPiece</span><span class="p">(</span><span class="nx">piece4</span><span class="p">))</span> <span class="nx">workspace</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="k">new</span> <span class="nx">State</span><span class="p">(</span><span class="nx">board</span><span class="p">.</span><span class="nx">withNextPiece</span><span class="p">(</span><span class="nx">piece4</span><span class="p">),</span> <span class="nx">nextPieces</span><span class="p">));</span>
        <span class="p">}</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div>

<p>There&#39;s obviously quite a bit more code that defines the models for pieces and boards, as well as a test suite I used while writing the logic. I can&#39;t promise that it&#39;s bulletproof, so please let me know if you find any mistakes.</p>

<p>I had a lot of fun working on this problem, and I hope it was at least half of an interesting read, even if search algorithms are old news to you.</p>

<p>Cheers, and Happy New Year!</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Five Less Mushy Technical Interview Tips]]></title>
<link href="http://jetheis.com/blog/2011/12/08/five-less-mushy-technical-interview-tips/"/>
<updated>2011-12-08T03:30:00-05:00</updated>
<id>http://jetheis.com/blog/2011/12/08/five-less-mushy-technical-interview-tips</id>
<content type="html"><![CDATA[<p>First, a disclaimer: I am <strong>in no way</strong> <em>any</em> kind of an expert on interviewing.  I&#39;m writing this because I&#39;m completing my senior year of undergraduate engineering school and I&#39;ve watched myself, my friends, and my peers go through the internship/new hire interview process for lots of companies with vastly different results. I&#39;m simply interested in making note of what seems to work, and what doesn&#39;t seem to work, specifically within the scope of programming students applying for actual real world programming jobs.</p>

<p>This post has nothing to do with me other than the fact that my experiences are one of the data points. In fact, this could probably more accurately titled <em>Things I Wish Someone Had Sat Me Down and Told Me Four Years Ago</em>.</p>

<p>Before we begin, let&#39;s assume that you&#39;ve already heard the standard job interview advice: be yourself, dress up, make eye contact, ask them questions, etc. I&#39;m not proposing that we throw out any of the age-old wisdom of folks in university career services offices. Rather, I&#39;d simply like to offer up a few addenda:</p>

<!-- more -->

<hr>

<h3>1. Don&#39;t be Yourself</h3>

<p>Yeah, yeah, I know I just said I&#39;m not contradicting old advice, but trust me, I&#39;m not. Hear me out on this one.</p>

<p>When an advisor says to &quot;be yourself&quot; in an interview setting, it makes it sound like you ought to be the person you are every day in engineering school. There&#39;s just one problem: that person is basically a schizophrenic shut-in with sarcasm tourettes. No offense; we&#39;re all like that. The only problem is that nobody wants to hire Jimmy Weirdo, no matter how good of a programmer he is.</p>

<p>So how do we fix it? Pretend to be someone totally different? Nope. Try this: take a couple of minutes and think about what you&#39;re going to be like in 5 years. You&#39;ll still be <em>you</em>, but you&#39;ll also be a mid-twenties professional developer. That&#39;s the guy this company wants to hire, and that&#39;s the guy you need to start acting like.</p>

<p>Now, let&#39;s be clear. I&#39;m not saying that you need to act like you&#39;ve already hit your midlife crisis to get people to like you. Rather, I&#39;m saying that it&#39;s profoundly uncommon for a company to meet a programming student who is demonstrating that he is ready and willing to grow up.</p>

<p>So how do we do this? Here&#39;s my running list:</p>

<ol>
<li>Get a haircut. A good one.</li>
<li>Shave. Zero hair coming out of your neck.</li>
<li>Buy some <em>stylish</em> business casual clothes. I know, I&#39;m horrible at this too.
Get a girl (not your mom, or your aunt) to help you.</li>
<li>Practice wearing those clothes. Learn to tie a tie loose enough that your
eyes don&#39;t bug out when you sit down (you know exactly what I&#39;m talking
about). Adults are comfortable in clothes.</li>
</ol>

<p>And most of all, just <em>act like an adult</em>. I&#39;ll cover it more in a second, but seriously, just think about how you&#39;re probably going to act in a few years when you don&#39;t have Mountain Dew dripping off your teeth and try to emulate that.</p>

<hr>

<h3>2. Know Your Stuff</h3>

<p>If there&#39;s one thing that will get you not hired, no matter how good of a programmer you think you are, it&#39;s not knowing the fundamentals. Whether you call yourself a PHP developer, an embedded systems programmer, or a system administrator, if you&#39;re showing up to a technical interview for a developer position, you absolutely will be asked the fundamental questions. It&#39;s incredible how many smart kids blow these. I&#39;ll admit that I&#39;ve been one of them.</p>

<p>So what do you need to know? Well, that probably depends on what you&#39;re applying for, but I do have a running list of things that you should bring with you to a technical interview:</p>

<ol>
<li><p><a href="http://en.wikipedia.org/wiki/Polymorphism_in_object-oriented_programming">Polymorphism</a>.  If you aren&#39;t able to basically recite what polymorphism is, don&#39;t bother showing up.</p></li>
<li><p><strong>Memory layout</strong>. Seriously. If you don&#39;t know the <a href="http://stackoverflow.com/a/80113">difference between the stack and the heap</a>, you might be about to look pretty stupid.</p></li>
<li><p><strong>Access modifiers</strong>. Specifically <code>public</code>, <code>private</code>, etc., as well as <code>static</code>, and sometimes more language-specific modifiers like <code>abstract</code> and <code>virtual</code>. Even if you don&#39;t use a language that uses them, know what they mean.</p></li>
<li><p><strong>Data structures</strong>. Know these inside and out. Literally. Know how they work, the efficiency of the operations on them, and what they&#39;re useful for. Start with this list:</p></li>
</ol>

<ul>
<li><strong>Lists</strong>. <a href="http://en.wikipedia.org/wiki/Dynamic_array">Array-based</a> and
 <a href="http://en.wikipedia.org/wiki/Linked_list">linked</a>. Know the difference.</li>
<li><a href="http://en.wikipedia.org/wiki/Stack_(abstract_data_type)">Stacks</a></li>
<li><a href="http://en.wikipedia.org/wiki/Queue_(abstract_data_type)">Queues</a></li>
<li><a href="http://en.wikipedia.org/wiki/Hash_table">Hash Tables</a> (the answer to 80% of programming questions)</li>
<li><p><a href="http://en.wikipedia.org/wiki/Binary_search_tree">Binary Search Trees</a> (likely used as a <a href="http://en.wikipedia.org/wiki/Set_(computer_science)">set</a>, which you should also know)</p>

<p>I&#39;ve also used <a href="http://en.wikipedia.org/wiki/Trie">Tries</a> and <a href="http://en.wikipedia.org/wiki/Heap_(data_structure)">Heaps</a> from time to time in interviews, so those are great to know as well.  </p></li>
</ul>

<ol>
<li><p><strong>Sorting</strong>. Know <a href="http://en.wikipedia.org/wiki/Merge_sort">Merge Sort</a>, <a href="http://en.wikipedia.org/wiki/Quicksort">Quicksort</a>, and <a href="http://stackoverflow.com/a/680559">the difference</a>. It also may be useful to know a few of the less efficient but more common ones (<a href="http://en.wikipedia.org/wiki/Bubble_sort">Bubble Sort</a>, <a href="http://en.wikipedia.org/wiki/Selection_sort">Selection Sort</a>, <a href="http://en.wikipedia.org/wiki/Insertion_sort">Insertion Sort</a>, etc.) so you can quickly identify them in existing code.</p></li>
<li><p><strong>Searching</strong>. Know how to search the various data structures that you have in your utility belt, but also know something about solution finding (searching states in a problem). Specifically things like pathfinding in a maze, collecting rewards, solving sliding puzzles, etc. Know the difference between <a href="http://en.wikipedia.org/wiki/Breadth-first_search">bredth-first</a> and <a href="http://en.wikipedia.org/wiki/Depth-first_search">depth-first</a> search, and be able to implement them quickly. If you&#39;re applying for a very technical job, you&#39;re probably going to need to know more.</p></li>
<li><p><strong>Optimizations and efficiency</strong>. If I had a nickel for every time I heard, &quot;Okay, but how can we make it faster?&quot; Know how to pick a faster data structure, be able to speak intelligently about your decisions. Also know a thing or two about <a href="http://en.wikipedia.org/wiki/Memoization">memoization</a>.</p></li>
<li><p><strong>Database structure</strong>. One kind of question that really seems to sneak up on students is the dreaded <em>design question</em>, usually worded something like, &quot;So, take a few minutes and describe how you&#39;d design eBay.&quot; Well, take some time and think about it. It basically comes down to being able to design good <a href="http://en.wikipedia.org/wiki/Domain_model">Domain Models</a>, <a href="http://en.wikipedia.org/wiki/Entity-relationship_model">ER Models</a> , and knowing a thing or two about <a href="http://en.wikipedia.org/wiki/Database_normalization">database normalization</a>.</p></li>
<li><p><strong>Design patterns</strong>. Ever heard of the Gang of Four? If not, time to put on your reading hat and learn something. If there&#39;s one thing that can really make you seem like a professional in interviews, I beleive that this is it.  If you&#39;re unfamiliar, <a href="http://en.wikipedia.org/wiki/Design_Patterns#Creational_patterns">start here</a>.  Also make sure you&#39;re familiar with <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC</a>.</p></li>
<li><p><strong>Other interview questions</strong>. Go hunting for classic technical interview questions. There are scores of them online, ranging from brain teasers to full blown implementation problems. Get good at thinking on your feet and solving these in 10-20 minutues or so.</p></li>
</ol>

<p>There are tons more, but that should really get you started and prepare you for 80%+ of interview questions (and honestly probably make you immediately more qualified than half of your peers). In case you&#39;re still interested, a few that I left out are <a href="http://en.wikipedia.org/wiki/Software_development_methodology">development methodologies</a>, <a href="http://en.wikipedia.org/wiki/Software_testing">testing strategies</a>, and <a href="http://en.wikipedia.org/wiki/Dynamic_programming">dynamic programming</a>.</p>

<p><strong>CAVEAT</strong>: Although I&#39;ve really tried to stress the importance of having a strong foundational knowledge going into your interview, one critical thing to remember is that under no circumstances should you pretend to know something you don&#39;t.  You&#39;re only digging your own grave at that point. Unfamiliar with a term or concept? Just say so. You may be surprised the good conversation that comes out of it, and it may be the difference between you solving a problem and you looking like an idiot.</p>

<hr>

<h3>3. Don&#39;t be an Ass</h3>

<p>This one. I&#39;m pretty convinced that this is the number one reason smart people don&#39;t get good jobs. This is also one of the hardest things to do, because when it comes down to it, you have no idea that you&#39;re being an ass. I sure didn&#39;t.</p>

<p>Fundamentally, it&#39;s a matter of respect. If you can&#39;t respect the people who are interviewing you, you&#39;re unhirable. There are a lot of ways you can be incredibly disrespectful without even knowing it, so let&#39;s look at a few cases.</p>

<p>First, let&#39;s get one thing straight: you&#39;re not doing the company a favor. If anything, you are <strong>blessed</strong> that your inherent social awkwardness drove you into a field that happened to explode with jobs during your lifetime. Remember what you&#39;re doing: you&#39;re asking someone to consider you qualified enough to sign over large sums of money to you twice a month in return for time and effort spent in an office. That&#39;s not a favor.</p>

<p>&quot;Yeah, but I&#39;ll make them so much more than they&#39;ll pay me.&quot; Yeah, that&#39;s the idea, and it&#39;s called being a business. Don&#39;t flatter yourself into thinking you&#39;re some kind of anomaly.</p>

<p>&quot;Yeah, but I&#39;m a rockstar programmer! Nobody in the whole class can sling code like me!&quot; That may be.  And that should make you the number one pick for the position, right? Wrong. The only person who can probably see that you&#39;re a great programmer is the developer conducting the interview, but he&#39;s not so much interested in money-making potential like HR is. He&#39;s hiring a team member who he may have to work with for the next 10 years. He can teach a mediocre developer the ways of the company, but he can&#39;t make you less unbearable. </p>

<p>Here&#39;s another one of my favorites: an interviewer is giving you the rundown of the company&#39;s software architecture, and he mentions a specific piece of technology/framework/library/whatever that you used at one time but don&#39;t anymore. You, being the <em>extremely experienced</em> developer, instantly say, &quot;Ew!&quot; and smirk like you&#39;re better than someone. Come on. First of all, you have <strong>no idea</strong> what sort of design and architecture decisions went into selecting this particular piece of technology, and your largest code base to date probably hasn&#39;t crossed 1000 lines of code, so you wouldn&#39;t even be qualified to speak on it if you <em>did know</em> the reasoning behind it.</p>

<p>I don&#39;t want to rant too much, but I do want to make this clear: I&#39;ve seen more opportunity lost due to self-entitlement than I have due to lack of qualifications.  If you walk in acting like you own the place, you&#39;ll come up short every time.</p>

<hr>

<h3>4. Be Interesting</h3>

<p>Okay, let&#39;s take a break for a second. If you can master the previous three points, you&#39;re in the top slice, and you&#39;ll get job offers. No questions asked.  From what I&#39;ve seen, there are just a couple more things that really set you apart in the extremely competitive positions, so I think it&#39;s good to cover those as well.</p>

<p>One of the most important things you can do to win the really desirable positions is to be different from your competition. It seems obvious, but it&#39;s something that&#39;s largely lacking from those entering the professional world.</p>

<p>Now, I don&#39;t mean different like &quot;I only wear sandals, because I don&#39;t believe in shoes.&quot; That&#39;s obnoxious. I mean different in that you&#39;ve followed your personal technical passions, relevant to the position or not, and done something on your own without anybody telling you to. Did you make a website? Sweet. An app? Very sweet. Anything at all (<em>sigh</em>, yes, even a game) that you&#39;ve done on your own really shows a ton about the kind of person you, and it gives you plenty to talk about in your interview.</p>

<p>Another easy way to be interesting is to <strong>read</strong>. Read <a href="https://news.ycombinator.com/">Hacker News</a>. Read <a href="http://www.reddit.com/r/programming">/r/programming</a>. Read <a href="http://slashdot.org/">Slashdot</a>. Read <em>something</em>. One of the most satisfying feelings is being knee-deep in an interview with a company that uses all sorts of technologies you&#39;ve never heard of and having your interviewer mention something you&#39;ve read an article about. Being able to contribute that little information (as long as it&#39;s not some sensationalist blog article) sometimes completely changes the dynamic of the interview for the better.</p>

<p>Sometimes even more important than reading things is <em>trying things</em>. Take any opportunity you get to try out a new tool, framework, or language. Even if you never do anything useful with it, you just added something to your interview tool belt, and you might even be able to answer questions about it.</p>

<p>One way or another, it&#39;s important for you to be different from your peers. It seems that more and more employers are hiring batches of students from the same colleges, so being the one guy whose resume doesn&#39;t look exactly like his transcript is probably the biggest favor you can do yourself.  Heck, it&#39;s about the only point in this post that&#39;s any fun, so why not?</p>

<p><strong>EDIT</strong>: A great piece of advice from <a href="https://plus.google.com/101837208071574214893">Kenny Gao</a>:</p>

<p>I think it&#39;s worth adding that a big part of Being Interesting (or more generally, being memorable) is being knowledgeable about the company that you&#39;re interviewing with. I&#39;m always surprised at the positive reaction that I get from interviewers when I ask them about something that&#39;s unique to their company that they&#39;re passionate about, and it makes for a great entry point into a meaningful discussion.</p>

<hr>

<h3>5. Be Charming</h3>

<p>This final point is probably the hardest one to advise on, but it seems to really make a difference when it comes down to it. You probably know the situation all too well: someone who&#39;s half as qualified as you but is just so damn likeable that nobody can say no to him gets an offer before you. It&#39;s not sneaky or deceitful; it&#39;s just the honest nature of people: they hire who they like.</p>

<p>If you&#39;re one of these lucky folks, congratulations; you&#39;re already probably basking in a wonderful life. As for the rest of us, let&#39;s do our best to emulate some of this charming behavior.</p>

<p>Why? Well, if there&#39;s one thing you learn from interviewing, it&#39;s that there will always come a time when the ship is sinking. It&#39;s these times when the only thing you&#39;ve got left to save you is a grin, a little joke, and as much charm as you can muster up. It&#39;s not that the charming guys among us don&#39;t make mistakes; it&#39;s just that their mistakes seem so insignificant since we like them so much.</p>

<p>I really don&#39;t have any great advice for how to be more charming. It&#39;s something I don&#39;t think I ever really got a good hold of.  If you can find some way to channel your inner Sinatra though, you&#39;ve got the world by the tail.</p>

<hr>

<p>Well, that about wraps things up. Again, this is in no way a definitive list of interview tips. Rather, it&#39;s just a collection of things that get said a lot but never officially written down. If it helps even one person get a job, it&#39;ll be entirely worth the time it took to jot it down.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Accelerating Rose-Hulman Schedule Lookup with Google Chrome]]></title>
<link href="http://jetheis.com/blog/2011/11/28/accelerating-rose-hulman-schedule-lookup-with-google-chrome/"/>
<updated>2011-11-28T21:21:00-05:00</updated>
<id>http://jetheis.com/blog/2011/11/28/accelerating-rose-hulman-schedule-lookup-with-google-chrome</id>
<content type="html"><![CDATA[<p>Yes, this is a Rose-Hulman specific post. If you&#39;re not a member of the campus community, this is not the post for you.</p>

<p><a href="https://plus.google.com/102157916895641654419">Mike Lester</a> gets the credit for this idea. I wasn&#39;t aware of this feature in Chrome until he showed me. So thank him.</p>

<p>If you&#39;ve been a student at Rose-Hulman for more than a day, you know all too well the blessing and curse that is the Schedule Lookup Page. We&#39;re all thankful that our college can be so close knit and trusting that we can share this information with each other, but we also generally don&#39;t like the schedule lookup page. I don&#39;t need to list complaints here, but one of the biggest ones is the number of clicks and the amount of typing it takes to find any one student&#39;s schedule.</p>

<p>The good news is that (because of Google Chrome&#39;s awesome features) we can speed up your boring old Schedule Lookup workflow and make you a real Rose-Hulman power user, and it will look something like this:</p>

<p><img src="http://img.jetheis.com/8e8d40af2f5b1fa78f518697258faa888540b852.png" width="484" height="98" /></p>

<p>To accomplish this, we&#39;ll set up a <em>Custom Search Engine</em> in Google Chrome that will let us search the schedule database directly. It&#39;s pretty easy, so let&#39;s get started.</p>

<!-- more -->

<p>First, open the preferences pane in Chrome (<code>⌘,</code> on a Mac, or by clicking the wrench and selecting <strong>Preferences</strong> (<strong>Options</strong> on some systems).</p>

<p>Once inside the preferences pane, click the <strong>Manage Search Engines...</strong> button.</p>

<p><img src="http://img.jetheis.com/c34edb41af589974fe9441ad6dd4d3f945a9f604.png" width="586" height="105" /></p>

<p>You should see two sets of search engine entries, <strong>Default search options</strong> and <strong>Other search engines</strong>. At the bottom of the <strong>Other search engines</strong> box, make a new entry as shown below:</p>

<p><img src="http://img.jetheis.com/f30dd5f06b9482f462209afea411ffd81d135cc9.png" width="646" height="242" /></p>

<p>(Here&#39;s the link to copy and paste into the third field)</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">https://prodweb.rose-hulman.edu/regweb-cgi/reg-sched.pl?termcode=201220&amp;view=tgrid&amp;id1=%s&amp;bt1=ID%2Username
</code></pre></div>
<p>Let&#39;s review what we just did. We created a new search engine that will be triggered when we enter the keyword <code>schedule</code>, followed by a space, in the address bar. This new search engine will display its name as <strong>Schedule Lookup</strong>. If you&#39;d like to change either of these to something you like better, feel free; you won&#39;t hurt anything as long as you don&#39;t use a name that&#39;s already taken by another search engine.</p>

<p><strong>IMPORTANT</strong>: If you&#39;re following these instructions and it&#39;s not Winter Quarter 2011, you&#39;re going to need to modify the URL pasted in the third field slightly: the six digit number after <code>termcode=</code> in the URL will need to be changed to the current Rose-Hulman term code (in the posted URL, it&#39;s <code>201220</code>).  If you&#39;re unfamiliar with how Rose-Hulman term codes work, here&#39;s a reminder (remember not to put a space in your actual term code):</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">2012 20
 |    | 
 |    |- 10 times the current term number (1 for fall, 2 for winter, 3 for spring)
 |    
 |- The calendar year this academic year ends in
</code></pre></div>
<p>Once you&#39;ve made any changes, it&#39;s time to try this thing out. Close your preferences pane and type <code>schedule someusername</code> in your address bar. You should see it automatically switch to say <strong>Search Schedule Lookup</strong>, and you should land on the appropriate schedule page when you hit Enter. You should also be able to search for partial names, just like on the regular Schedule Lookup Page.</p>

<p><strong>One more thing</strong>: While we&#39;re at it, let&#39;s solve one more annoying problem with the Schedule Lookup Page: the fact that you can&#39;t seem to get a link directly to your own schedule. Take the link below, change <code>TERMCODE</code> to the term code you figured out in the last section and <code>USERNAME</code> to the username you want to link to, and voilà! You&#39;ve got a link directly to a schedule page.</p>
<div class="highlight"><pre><code class="text language-text" data-lang="text">https://prodweb.rose-hulman.edu/regweb-cgi/reg-sched.pl?termcode=TERMCODE&amp;view=tgrid&amp;id1=USERNAME&amp;bt1=ID%2Username
</code></pre></div>
<p><strong>EDIT</strong>: Will Anderson created a <a href="http://www.itsananderson.com/2011/11/creating-a-custom-chrome-search-engine/">custom chrome search engine</a> with even more functionality, hosted on his blog. If you&#39;re looking for more flexibility beyond what this post describes, check out his post.</p>

<p><strong>EDIT FOR FIREFOX USERS</strong>: Tom Most created a <a href="http://www.userscripts.org/scripts/show/36915">GreaseMonkey Script</a> that automatically makes schedule pages &quot;bookmarkable&quot; when you reach them from the schedule lookup search page. If you&#39;re a Firefox/GreaseMonkey user, check it out.</p>

<p>Cheers,</p>
]]></content>
</entry>

<entry>
<title type="html"><![CDATA[Yet Another Reload]]></title>
<link href="http://jetheis.com/blog/2011/11/24/yet-another-reload/"/>
<updated>2011-11-24T04:54:00-05:00</updated>
<id>http://jetheis.com/blog/2011/11/24/yet-another-reload</id>
<content type="html"><![CDATA[<p>Here we go again. If you&#39;re someone who has ever followed my blogs in the past, you know that about once a year I just scrap everything and start over, usually due to a system upgrade or because I broke something. Well, that time of the year has come again, but for some slightly different reasons.</p>

<p>First off, I have noticed this pattern of never having a blog that&#39;s more than a year old, and I do see it as a potential problem, because I may eventually have something interesting to say that may be useful more than eight months in the future. I&#39;m not saying that it&#39;s happened yet, but I&#39;m not ruling out the possibility.</p>

<p>Second, I think I&#39;m finally growing out of this idea that I need to play system administrator for all the things I depend on, so I&#39;ve decided to move my blog to &quot;The Cloud&quot; where it can be safe from my sometimes very stupid hands. This is probably the best decision I&#39;ve made in a while. Part of this decision includes switching from <a href="http://wordpress.org">Wordpress</a> to <a href="http://octopress.org">Octopress</a>, which I have to admit, makes me feel very comfortable writing posts so far.</p>

<p>Third, I did have a lot of posts about my work at Microsoft as a summer intern, and while there was nothing particularly top secret about the content of those posts, it&#39;s probably easier if I don&#39;t claim responsibility for things I say or infer about the company, especially now that I&#39;ll be going to work full time for a company besides Microsoft. I&#39;m sure I&#39;ll mention it from time to time, but we&#39;ll take that as it comes.</p>

<p>I&#39;m excited to be making big decisions about my social media life, and I really like the Octopress platform so far. I&#39;m sure I&#39;ll have more to say about it, and I imagine I&#39;ll configure it some more and discuss my thoughts on it in the future once I&#39;ve actually used it some more.</p>

<p>Cheers, and Happy Thanksgiving.</p>
]]></content>
</entry>

</feed>
