tag:blogger.com,1999:blog-9298519850195590532024-02-20T19:34:44.990-05:00Silica Gel: Do Not EatJames Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.comBlogger35125tag:blogger.com,1999:blog-929851985019559053.post-1442539337579420782018-01-14T22:17:00.000-05:002018-01-16T15:32:47.698-05:00Egressors - those who escapeCharlene and I have really gotten into escape rooms lately. I mean, <i>really</i> gotten into them. We did our first room in September of 2016, and since then we've been playing as many as we can. So far we've done about 26 rooms in 5 states and 3 countries. If you haven't tried them yet, I just can't recommend them enough.<br />
<br />
We started a spreadsheet to track which rooms we've visited, and it wasn't long before I thought there ought to be a website for that. I've seen at least one (<a href="https://escapetalk.nl/en/">escapetalk.nl</a>), but it's mostly in Dutch and doesn't cover our area. After a while I decided this was something I could do. So after a few months of on-and-off coding while the kids were asleep, I'm finally ready to launch:<br />
<br />
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: x-large;"><a href="https://www.egressors.com/">EGRESSORS.COM</a></span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5p4VWvuhSfYhyF82LApmOH-aNgRJgax6nS-Sjg0am4gArWzwp3y1lDaZgHLUkoEfjhyphenhyphentgBBnquXjg1v5MjhLYYr2kG1PcnmhCHe_-SoPUPpOAa38Ioy0LjtyGsAZ4nd4pGPhMgwj1Z5Q/s1600/egressors.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="64" data-original-width="92" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5p4VWvuhSfYhyF82LApmOH-aNgRJgax6nS-Sjg0am4gArWzwp3y1lDaZgHLUkoEfjhyphenhyphentgBBnquXjg1v5MjhLYYr2kG1PcnmhCHe_-SoPUPpOAa38Ioy0LjtyGsAZ4nd4pGPhMgwj1Z5Q/s1600/egressors.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Egressors Logo</td></tr>
</tbody></table>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: left;">
Egressors is a directory of escape rooms in the New England area, and a place where you can sign in and track your own escapes. We're starting simple and planning to build up features over time. For instance, right now you can track your escapes but you can't see anyone else's.</div>
<div style="text-align: left;">
<br /></div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFi9jOvCTRhJinvtHuAEHwfxKjKE_mCRaCRHxZdM7KZcQjO_5H7d0f7hBz9wwyZUNDw7k8du10V0nmTVOwhrMCk8o3GkLVFipCDTBWDs7TbXcRHnzAabFWpQZN1OCLtWoM13V0hbM5wbw/s1600/wordlock.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="230" data-original-width="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFi9jOvCTRhJinvtHuAEHwfxKjKE_mCRaCRHxZdM7KZcQjO_5H7d0f7hBz9wwyZUNDw7k8du10V0nmTVOwhrMCk8o3GkLVFipCDTBWDs7TbXcRHnzAabFWpQZN1OCLtWoM13V0hbM5wbw/s1600/wordlock.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fancy graphics.</td></tr>
</tbody></table>
<div style="text-align: left;">
One of the first updates I plan for the site will be to see how many users have logged plays at each room, and see the escape rates. I'm holding back on reviews and comments and other judgement-y features at first, because I don't want the site to impact any of the venues in a negative way.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Another early update I hope to include will be a tailored news system - so that users can sign up to hear about new rooms and events, but specifically ones in their area or for rooms they are "watching". Some rooms have their own mailing lists, but you have to sign up to those one at a time. There are mailing lists out there for general escape room info, but they don't have any way to filter to local information.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
The other escape-room-related project we're working on is a "passport" for rooms in the North Shore area. There's a great concentration of rooms along I-95 north of Boston, and if other "egressors" are like me, then once you're hooked you want to escape them all. This passport provides a physical way to track where you've gone, and a goal greater than just one room.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifXIqmQTCUOVaQesjLzt3u-4JlViiQu70p6PU5vKmLq_Ccz_PSY6_HWsL9UUS4-c2AWdaTrMKnVhf-iiT-QxTfwB_ybCkgCRv1DwxJzTqA5jvaWhXfjlmJt34_d6MZfzfo1zjDU51iot0/s1600/passport-2018-north-shore.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="200" data-original-width="340" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifXIqmQTCUOVaQesjLzt3u-4JlViiQu70p6PU5vKmLq_Ccz_PSY6_HWsL9UUS4-c2AWdaTrMKnVhf-iiT-QxTfwB_ybCkgCRv1DwxJzTqA5jvaWhXfjlmJt34_d6MZfzfo1zjDU51iot0/s320/passport-2018-north-shore.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Not valid for travel to foreign countries.</td></tr>
</tbody></table>
Plus, and of course, there's a puzzle on it. To solve the puzzle you need to visit enough of the rooms (not necessarily all of them). Each room will write something different that will get you one step closer to the solution. Nine venues in the North Shore area have signed up to distribute the passport, all fine places to get out of:</div>
<div style="text-align: left;">
</div>
<ul>
<li><a href="http://www.8droomescape.com/">8D Room Escape and Board Games</a></li>
<li><a href="http://amazeescape.com/">Amaze Escape</a></li>
<li><a href="https://breakoutgames.com/boston/">Breakout</a></li>
<li><a href="http://www.escapethislive.com/boston/">Escape This Live</a></li>
<li><a href="https://www.escapology.com/en/tewksbury-ma/">Escapology</a></li>
<li><a href="http://www.keyquest-escaperoom.com/locations/escape-room-ma-danvers.cfm">Key Quest at Laser Quest</a></li>
<li><a href="https://northshoreescape.com/">North Shore Escape</a></li>
<li><a href="http://www.timewarprooms.com/">Time Warp</a></li>
<li><a href="http://www.wickedescapes.net/">Wicked Escapes</a></li>
</ul>
<div>
At some point I might add a feature to egressors.com where you can enter the solution to the passport and thus get some internet points. If the passport goes well I could see expanding it to other areas.</div>
<div>
<br /></div>
<div>
So if you're in New England and like escape rooms, check out the site and consider signing up for an account. And if you're near the North Shore then keep an eye out for the passport.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-50842647770659820262017-12-15T23:26:00.000-05:002017-12-15T23:26:07.317-05:00QblePon is the best game<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn8Q2VB_f8mOfMvebDy3MTqrqMg0thNJwIITSXEf_sGWBbBmwebkspEp_IpDPjUuUWoVT4-iWTstXlQWktqz3PYZeKtQRzfDM3pIny8koxApf8K3WO4UZKPAlM6Qtm75SCZeV1t8x-WWQ/s1600/qblepon.PNG" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="168" data-original-width="191" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn8Q2VB_f8mOfMvebDy3MTqrqMg0thNJwIITSXEf_sGWBbBmwebkspEp_IpDPjUuUWoVT4-iWTstXlQWktqz3PYZeKtQRzfDM3pIny8koxApf8K3WO4UZKPAlM6Qtm75SCZeV1t8x-WWQ/s1600/qblepon.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The best game.</td></tr>
</tbody></table>
I want to fix a problem with the internet. First of all, I think we can all agree that <a href="https://www.youtube.com/watch?v=3zf0tRZC3q0" target="_blank">QblePon</a> is the best game.<br />
<br />
But the thing is, I searched for a list of QblePon characters on the internet and as far as I can tell there isn't one. This is ridiculous, people. There's a list of <a href="https://en.wikipedia.org/wiki/List_of_animals_with_fraudulent_diplomas" target="_blank">animals with fraudulent diplomas</a>, for Pete's sake. Where is someone to go when they can't remember which QblePon character has the floppy disk in its head? (It's Stanchmode)<br />
<br />
So I'm here to help. Please enjoy below a list of all the known QblePon characters. There are probably more, but these are the ones that Hector mentioned when he described the epic game that he and This Kid played, which come to think of it may be the <i>only</i> game of QblePon ever played.<br />
<br />
Before we begin, a little bit about the game itself. It's a card game. We know that two players can play it, and they sort of mostly take turns. The characters seem to have a single stat: Something the range of one to five. Also, in <a href="https://www.youtube.com/watch?v=9MrPnPpIg5k" target="_blank">this episode of the TV show</a>, it is revealed that Twirbirdler can do an attack called "Gashing Slice". It is not known how the attack works in the game.<br />
<br />
Well that's enough details, let's see some characters. You know you want to look at them. Here they are in alphabetical order:<br />
<br />
<div style="text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh0ahCNpZsCWxPoCsDVnvH1skGj2bUgPx7W7c1KdslaM4PHbDXJ1sXEeN9vv-zB0psavAaWTDogAk0PwEVqifF6No9nvYK3RMJVxC-Ekz7j3Qt0i-Kxnd2gNeS8j4zEZd8q0i3RpA-dQQ/s1600/beestburdin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="223" data-original-width="219" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgh0ahCNpZsCWxPoCsDVnvH1skGj2bUgPx7W7c1KdslaM4PHbDXJ1sXEeN9vv-zB0psavAaWTDogAk0PwEVqifF6No9nvYK3RMJVxC-Ekz7j3Qt0i-Kxnd2gNeS8j4zEZd8q0i3RpA-dQQ/s200/beestburdin.PNG" width="196" /></a></div>
<div style="text-align: center;">
Beestburdin</div>
<div style="text-align: center;">
Stat: 5</div>
<div style="text-align: center;">
"My main man and yours."</div>
<div style="text-align: center;">
Hard to stop thinking about.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixHdgj21-WONxfMzKsAG35_t3o7OrnaC88sCHzfOKJubBy9CC2HVxQx7u3x_pqOj2UhYqp2CniqcUj9Bbm4JLj0gmhVxsICA69g86P5pzL8lx2w1EFt_rBUW0vSYdnsx4uEQ8-BpGxYUY/s1600/feedems.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="297" data-original-width="316" height="187" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixHdgj21-WONxfMzKsAG35_t3o7OrnaC88sCHzfOKJubBy9CC2HVxQx7u3x_pqOj2UhYqp2CniqcUj9Bbm4JLj0gmhVxsICA69g86P5pzL8lx2w1EFt_rBUW0vSYdnsx4uEQ8-BpGxYUY/s200/feedems.PNG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Feedems</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<div class="separator" style="clear: both; text-align: center;">
"All day long."</div>
<div class="separator" style="clear: both; text-align: center;">
Combo: Good with Feedems (Jersey) and Feedems (Raw).</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRSQClpacXI09IqXiAAK0_VuIM-8ijapp_BxNhCbQqNK1GAdOBVGgpL5bR-tlK2a66D9mQhX6nn9SizgebGxws3_FFtWF0S9Trr1qvZpsJRTsooxo5h97x43cjfcKFM6QBbpTRBXahRY/s1600/feedems-jersey.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="199" data-original-width="183" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRSQClpacXI09IqXiAAK0_VuIM-8ijapp_BxNhCbQqNK1GAdOBVGgpL5bR-tlK2a66D9mQhX6nn9SizgebGxws3_FFtWF0S9Trr1qvZpsJRTsooxo5h97x43cjfcKFM6QBbpTRBXahRY/s200/feedems-jersey.PNG" width="183" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Feedems (Jersey)</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<div class="separator" style="clear: both; text-align: center;">
See also: Feedems.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdTAssixVZD6m77kSGTRV4bKonMRB1exN7LPq5XVY1N41iGmzRaVnaLyplmJR4xHBwmtZAsEAoi6tQmDBD-aVgwA874L-YqRLlnIAQT4ciiytw4nyWAC4_98C0f4cF1okk5qAxmOvxMcM/s1600/feedems-raw.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="196" data-original-width="201" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjdTAssixVZD6m77kSGTRV4bKonMRB1exN7LPq5XVY1N41iGmzRaVnaLyplmJR4xHBwmtZAsEAoi6tQmDBD-aVgwA874L-YqRLlnIAQT4ciiytw4nyWAC4_98C0f4cF1okk5qAxmOvxMcM/s200/feedems-raw.PNG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Feedems (Raw)</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<div class="separator" style="clear: both; text-align: center;">
See also: Feedems.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKd495XAHTNdO9w7lAPvCvzfFdsEGWveJKGv89NB-MisUwqUVYcDMxF8YYEHg3msXHNy6Hg5ol0XF5YDbMbY_wpNwi4ZLQ4W6cnaSheC3flwxAiNeZ2ZClc6Uxo2lfKsEzHtgfNLYdHc/s1600/jamtape.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="211" data-original-width="169" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKd495XAHTNdO9w7lAPvCvzfFdsEGWveJKGv89NB-MisUwqUVYcDMxF8YYEHg3msXHNy6Hg5ol0XF5YDbMbY_wpNwi4ZLQ4W6cnaSheC3flwxAiNeZ2ZClc6Uxo2lfKsEzHtgfNLYdHc/s200/jamtape.PNG" width="160" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Jamtape</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<div class="separator" style="clear: both; text-align: center;">
"Not actually a QblePon character."</div>
<div class="separator" style="clear: both; text-align: center;">
Actually, a QblePon character.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguffz77pw2U0nMc22jyrDRpbWSArEftgV63CQKmTUuUz1JcNtRkXCyJWauHs9_nuyM3Xzz7552SDBgdLd-dRQd-bv9bNKGKJ0lLtCKIDCGQITxcgspXbB4rCQH1hQH_2ip67p15hWmEbg/s1600/pantso.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="220" data-original-width="192" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguffz77pw2U0nMc22jyrDRpbWSArEftgV63CQKmTUuUz1JcNtRkXCyJWauHs9_nuyM3Xzz7552SDBgdLd-dRQd-bv9bNKGKJ0lLtCKIDCGQITxcgspXbB4rCQH1hQH_2ip67p15hWmEbg/s200/pantso.PNG" width="174" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Pantso</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 5</div>
<div class="separator" style="clear: both; text-align: center;">
Combo: Can be played with Skweezout.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb5I-lszsy47cxkvxYxQ01Eo5yg2tp2pAJTdPqxX1H1AEmmAi1nROY-tKqfsvglGa6Ou3PXrzc-u6gJQ0ikVIeBEU4lKM47E7y037v_IGtPAcjEPkz4-2kpO0xpsZ7IGRrQ6G_OnZSm_Q/s1600/puckershine.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="218" data-original-width="161" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgb5I-lszsy47cxkvxYxQ01Eo5yg2tp2pAJTdPqxX1H1AEmmAi1nROY-tKqfsvglGa6Ou3PXrzc-u6gJQ0ikVIeBEU4lKM47E7y037v_IGtPAcjEPkz4-2kpO0xpsZ7IGRrQ6G_OnZSm_Q/s200/puckershine.PNG" width="147" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Puckershine</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 1</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9jDSw9_NzKGpmUMvFnpZXlS5u6xeURymWIHxrmSjkYqufZI80C_8cp1h_oy2u39maAIYvn5YTs1m_EwJ11vDp8d2yNAzO-mzcgG00RuevJ4XN9eF2tSR-O9uzp1qXSzc-2oAGRzvoVrc/s1600/robustol%25C3%25A9.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="246" data-original-width="178" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9jDSw9_NzKGpmUMvFnpZXlS5u6xeURymWIHxrmSjkYqufZI80C_8cp1h_oy2u39maAIYvn5YTs1m_EwJ11vDp8d2yNAzO-mzcgG00RuevJ4XN9eF2tSR-O9uzp1qXSzc-2oAGRzvoVrc/s200/robustol%25C3%25A9.PNG" width="144" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Robustolé</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 1</div>
<div class="separator" style="clear: both; text-align: center;">
"Some fourth level beeswax."</div>
<div class="separator" style="clear: both; text-align: center;">
Not the best card to start with.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQUak3jlAvah8D18sdSDc0e1-EiT2iACJzyhHgaUX7BG-lcCr57tMRNjW7HqABHOWsU_-m7Ifa9CRF1UDY5Fwg7VpGq19MDOM2xoBJlWie9B_0q6EypHDhuB-wVWe8LsQERYaCdy3Kkpw/s1600/seemingly-sam.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="254" data-original-width="175" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQUak3jlAvah8D18sdSDc0e1-EiT2iACJzyhHgaUX7BG-lcCr57tMRNjW7HqABHOWsU_-m7Ifa9CRF1UDY5Fwg7VpGq19MDOM2xoBJlWie9B_0q6EypHDhuB-wVWe8LsQERYaCdy3Kkpw/s200/seemingly-sam.PNG" width="137" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Seemingly Sam</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 2</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc-7S9VLZOVQHt8m0TzxLUbHrqoaWAFc4z5g1vKlezzoMdz_iy97zHXr1LjbXExPtAoAwgEm67Lcq6HxuvhyTD80mPielwcMAtx4VpzaYBB-N8MFg5LYU-G-m0Fyt-mhzMr3dLmlbEwp0/s1600/skweezout.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="218" data-original-width="140" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgc-7S9VLZOVQHt8m0TzxLUbHrqoaWAFc4z5g1vKlezzoMdz_iy97zHXr1LjbXExPtAoAwgEm67Lcq6HxuvhyTD80mPielwcMAtx4VpzaYBB-N8MFg5LYU-G-m0Fyt-mhzMr3dLmlbEwp0/s200/skweezout.PNG" width="128" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Skweezout</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 2</div>
<div class="separator" style="clear: both; text-align: center;">
Combo: Can be played with Pantso.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieNX97LjOHN1pvevtPcMz8BugUeLO2JJ82m9h2pkjMBS7T85MqYg6Nsy11AjnecQQ1amHneZVjH-Ybd31VA0qesvwzOOS78y7hqplD-2rf7yl7und8IihbZQkTItZpHa-crhUosdt24UQ/s1600/stanchmode.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="238" data-original-width="175" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieNX97LjOHN1pvevtPcMz8BugUeLO2JJ82m9h2pkjMBS7T85MqYg6Nsy11AjnecQQ1amHneZVjH-Ybd31VA0qesvwzOOS78y7hqplD-2rf7yl7und8IihbZQkTItZpHa-crhUosdt24UQ/s200/stanchmode.PNG" width="147" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Stanchmode</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 5</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtkcye0-aDv5cozqrskmdPqKbDyrfFjXFIaMfrS1zD5JtR53RGnWqhsIVb95RKE_KEqgQYZvuSDtTk__FS9qdZBEwgx1xkbEo6d6q-352HQVf-gm9RKYGqsxATe1HVju5LkY89gG_XnNY/s1600/tallsocket.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="236" data-original-width="142" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtkcye0-aDv5cozqrskmdPqKbDyrfFjXFIaMfrS1zD5JtR53RGnWqhsIVb95RKE_KEqgQYZvuSDtTk__FS9qdZBEwgx1xkbEo6d6q-352HQVf-gm9RKYGqsxATe1HVju5LkY89gG_XnNY/s200/tallsocket.PNG" width="120" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Tallsocket</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 4</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgDeTYW2VdHsN_M_d_2le63xMijaiobF_-EKkgBu7q3PP5t7csDk3GYH8Yksb0ei3Z8teH7UmF2r3BzDFFqsCOcuTEPg1QxgSjLR9Qo0zAj9SkzSoAB9-rWyNg2F3JvUpEB4qo1zv4Oww/s1600/twirbirdler.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="230" data-original-width="145" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgDeTYW2VdHsN_M_d_2le63xMijaiobF_-EKkgBu7q3PP5t7csDk3GYH8Yksb0ei3Z8teH7UmF2r3BzDFFqsCOcuTEPg1QxgSjLR9Qo0zAj9SkzSoAB9-rWyNg2F3JvUpEB4qo1zv4Oww/s200/twirbirdler.PNG" width="126" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Twirbirdler</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<div class="separator" style="clear: both; text-align: center;">
Attack: "Gashing Slice"</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXDHosUIZrrJvYgWI7-ZeIQWpuX-YOffQhmc2fazOMReghSjatqDmyEZbQ86aYDd6lMhZw1NHuJe7ATZiEE2Ire1w9kG249qZvynWngjyd4c1T6YU9VMAiN_0VgyUc9do_gplQXVkaiHw/s1600/zappadasher.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="208" data-original-width="191" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXDHosUIZrrJvYgWI7-ZeIQWpuX-YOffQhmc2fazOMReghSjatqDmyEZbQ86aYDd6lMhZw1NHuJe7ATZiEE2Ire1w9kG249qZvynWngjyd4c1T6YU9VMAiN_0VgyUc9do_gplQXVkaiHw/s200/zappadasher.PNG" width="183" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Zappadasher</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 1</div>
<div class="separator" style="clear: both; text-align: center;">
May not have been played in Hector's game.</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicCh2RYwuPimFqLmquNDf1GVzVdxX8wNlShgydr2_SP2zc16bp3RnGxl9d89QGzSuuv5-vR9WF5laIff5JOikFSYUz3PrGEH7e86-HB4pmZOfzPsQ1RUk8zkl1AkD5oYyOarBOdJnhyphenhyphencQ/s1600/zock-click.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="182" data-original-width="214" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicCh2RYwuPimFqLmquNDf1GVzVdxX8wNlShgydr2_SP2zc16bp3RnGxl9d89QGzSuuv5-vR9WF5laIff5JOikFSYUz3PrGEH7e86-HB4pmZOfzPsQ1RUk8zkl1AkD5oYyOarBOdJnhyphenhyphencQ/s200/zock-click.PNG" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
Zock Click</div>
<div class="separator" style="clear: both; text-align: center;">
Stat: 3</div>
<br />
<br />
There, problem solved.<br />
<br />James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-80394778392494258652017-12-10T17:08:00.000-05:002017-12-15T23:28:28.274-05:00Mancala AIMy kids were into <a href="https://en.wikipedia.org/wiki/Mancala" target="_blank">mancala</a> for a little while this year, and so I made a version of it that you (mainly my kids) can play in a web browser. A secondary reason to do it was to explore more about the game and its strategies.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrls6e_U0Cdl6E29UHxs73sww25KPy7OlC_Tpc3EbsxDr57PQPa85kslgtJUVGPieKQgBrWiDldra58Kt0qLyZqxLzKZIwkcEFRWIRvAGVFF5g3GBKS0oAPSVHaAbX4V4-XgJ_71jESMg/s1600/mancala.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="404" data-original-width="982" height="131" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrls6e_U0Cdl6E29UHxs73sww25KPy7OlC_Tpc3EbsxDr57PQPa85kslgtJUVGPieKQgBrWiDldra58Kt0qLyZqxLzKZIwkcEFRWIRvAGVFF5g3GBKS0oAPSVHaAbX4V4-XgJ_71jESMg/s320/mancala.PNG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Looks like south has a pretty good lead here...</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
Here's some of what I learned from <a href="https://www.politesi.polimi.it/bitstream/10589/134455/3/Thesis.pdf" target="_blank">this paper: Design of Artificial Intelligence for Mancala Games</a>:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
There are around 650 billion board positions (depending on which rule set you use, here I'm focusing on "Kalah"). In the year 2000, someone at Caltech found the winning strategy for it - that is, they solved it so we can now know what the best move is in each position. The player that goes first has a slight advantage in random play. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
For a simple human-player strategy, we could learn from the "Advanced Heuristic Minimax", which uses the following principles, weighing the earlier ones more strongly than the later ones:</div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<ul>
<li>Maximize the amount of counters in your own store.</li>
<li>Keep the opponents score to a minimum.</li>
<li>Prefer to move the counters from your right-most pit.</li>
<li>Have as many moves as possible from which to choose.</li>
<li>Hoard as many counters as possible in the left-most pit.</li>
<li>Keep as many counters on your own side as possible.</li>
</ul>
<div>
Below is a link to the online version I made, which has four computer opponent choices: Random Rachel just picks each move randomly. Greedy Greg moves to take as many pieces as he can, choosing randomly when he can't capture. Minnie Max uses a minimax strategy, looking ahead about 8 moves. And then there's Foolish Frank, who also uses minimax, but makes the worst move he can find.</div>
<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="http://sbj42.github.io/projects/mancalamity/" target="_blank">Mancalamity</a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/sbj42/mancalamity" target="_blank">(github repo)</a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div>
<br /></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-69462027417578372452017-10-11T21:20:00.002-04:002017-10-11T21:20:11.471-04:00An unfinished Pikmin-like game prototype<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9iizxfItu-fpdGgLOl8H9uhwIYxxdBK6iu0GwDoHiMcm0hunI0jkji5ekTbhDnyJGXVcb4QA_k2RwXMONDhoghh0yrwIkB-WaPQr6wHCgDZozURnWp7AOfNfekZVuyA7kFHcxRythRQ/s1600/pikmin-like.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" data-original-height="364" data-original-width="248" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9iizxfItu-fpdGgLOl8H9uhwIYxxdBK6iu0GwDoHiMcm0hunI0jkji5ekTbhDnyJGXVcb4QA_k2RwXMONDhoghh0yrwIkB-WaPQr6wHCgDZozURnWp7AOfNfekZVuyA7kFHcxRythRQ/s320/pikmin-like.png" width="218" /></a>Here's a project that I haven't managed to finish. It's a 2D imitation of <a href="https://en.wikipedia.org/wiki/Pikmin" target="_blank">Pikmin</a>, which I think is a fascinating take on the real-time strategy genre. I was working furiously on it in March, and then I ran out of steam and moved on to something else. I was a little too ambitious here for a short-term project, but it was some good exercise for certain tools. I used the <a href="http://phaser.io/" target="_blank">Phaser</a> framework for the rendering and tile/map management, and <a href="http://www.mapeditor.org/" target="_blank">Tiled</a> to make the maps.<br />
<br />
To try it out, just click anywhere to get past the menu and then:<br />
<br />
<ul>
<li>W A S D to move your character</li>
<li>left-click to throw a minion</li>
<li>right-click (and hold) to call minions in a certain area</li>
<li>space to disband (and sort) your army</li>
</ul>
<div>
Throw minion onto a pellet to pick it up and carry it back to their home. They'll carry it to whichever color makes up the majority of the carrying crew. That will then spawn more minions.</div>
<div>
<br /></div>
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><a href="http://sbj42.github.io/projects/pikmin-clone-demo/" target="_blank">Pikmin-like game prototype</a></span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-25996624141059253862017-09-14T20:58:00.001-04:002017-09-14T20:58:18.143-04:00SporkFeed: command-line rss/atom feed reader<div class="separator" style="clear: both; text-align: left;">
I get really frustrated trying to read websites on my phone. I used to use Google Reader, but that died so I switched to Feedly. But I became frustrated with the ads, including some particularly invasive ones. So I just started cycling through sites, one bookmark at a time.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Even ignoring the extra work it takes to move from site to site, it's still not much better. Websites are full of ads too, and increasingly they seem to be videos or animated images. I just want to see a list of the article names, and maybe an excerpt of the article, so I can decide whether to skip it, read it, or save it for later. I don't need to download all those bytes.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So I thought about making my own (yet another) feed reader. What's different about this one? It runs on the command-line.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJUAYckzyTqygDv3anFjIOd6V-99PgpOP_2wa2m3AeUZWENZNAdbJvxp60_taNAwbSFbLV1Hg8n08FseLSiOVFjiwmj7k2nAQmYqf8pmsePYOinlRXUxLbFsAkiaOsthKy79Iy_vV-T98/s1600/sporkfeed.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="75" data-original-width="365" height="65" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJUAYckzyTqygDv3anFjIOd6V-99PgpOP_2wa2m3AeUZWENZNAdbJvxp60_taNAwbSFbLV1Hg8n08FseLSiOVFjiwmj7k2nAQmYqf8pmsePYOinlRXUxLbFsAkiaOsthKy79Iy_vV-T98/s320/sporkfeed.png" width="320" /></a></div>
<br />
<br />
SporkFeed is a command-line rss/atom feed reader. It loads articles from multiple websites and lets you read them in plain text. Here are some of the features:<br />
<br />
<ul>
<li>No central website or server - the feed data is under your control</li>
<li>You choose where to put the reader state (e.g. put it in DropBox so you can read from multiple devices)</li>
<li>Simple keyboard command interface</li>
<li>Launch articles in a web browser (where possible)</li>
<li>Open Source (<a href="https://github.com/sbj42/sporkfeed-cli" target="_blank">github</a>)</li>
<li>Cross-platform</li>
</ul>
<br />
<br />
<div>
The core of the reader is in a separate module, <a href="https://github.com/sbj42/sporkfeed-core" target="_blank">sporkfeed-core</a>, so in theory multiple user interfaces ca n be written against the same reader state. That would mean that the feed data could be shared between the command-line interface and a mobile interface. I'm thinking of making either a web interface or a mobile app, which would finally solve my phone reader problem.</div>
<div>
<br /></div>
<div>
This command-line version requires <a href="http://node.js/">node.js</a>. Once you have that, just run this to install it:</div>
<blockquote class="tr_bq">
<span style="font-family: Courier New, Courier, monospace;">npm install -g sporkfeed</span></blockquote>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhabJAz4KpxyTHWtDNW3gUKX3OkokQUoqW57xn9sftKkk13X7Cn25S_E_pdvDHzg5mtAv_alrClOUjL-XVOLr2_oz8ybNwppo_8wove6-xWZhaD1KXftB-hHztFlNRi-tmdy3QU__uejd0/s1600/sporkfeed-demo.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="437" data-original-width="649" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhabJAz4KpxyTHWtDNW3gUKX3OkokQUoqW57xn9sftKkk13X7Cn25S_E_pdvDHzg5mtAv_alrClOUjL-XVOLr2_oz8ybNwppo_8wove6-xWZhaD1KXftB-hHztFlNRi-tmdy3QU__uejd0/s1600/sporkfeed-demo.gif" /></a></div>
<br />
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><a href="https://github.com/sbj42/sporkfeed-cli" target="_blank">SporkFeed</a></span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-39851572136489939602017-07-13T22:01:00.000-04:002017-07-13T22:01:03.257-04:00Tower of Hanoi VariationsA long, long time ago (1993) I spent a few weeks at a <a href="http://www.doe.virginia.gov/instruction/governors_school_programs/summer_residential/index.shtml" target="_blank">summer residential governor’s school</a>, hosted by the college of <a href="http://www.wm.edu/" target="_blank">William and Mary</a>. I remember only fragments of that time: Playing capture-the-flag across the campus. Some kind of fancy asteroids game. Recording professors' answering machine messages and editing them to make them say funny things.<br />
<br />
During one programming class, I remember the professor describing a <a href="https://en.wikipedia.org/wiki/Linear_congruential_generator" target="_blank">linear congruential generator</a> for pseudorandom numbers. I thought to myself, "That can't be the best way, I came up with that on my own at home." Then, being young, I said that out loud. I think the professor said something to the effect of <a href="https://www.youtube.com/watch?v=hssPVz33aDE" target="_blank">"Yes, you're very smart, now shut up."</a><br />
<br />
I also remember helping to solve a variation of the Tower of Hanoi, and getting my name in a <a href="http://www.cs.wm.edu/~pkstoc/gov.pdf" target="_blank">published paper</a>.<br />
<br />
You should already know what the Tower of Hanoi puzzle is. If not, you know how to use Google: I'm not going to describe it to you here.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-88xnjHk5hPnX_wrH-ZQn_M90p8qbAzFon3VoAlxdL_J3mvEO96UTlWP0QDIg-aXtDnFxjAeFBAIJaxHFzTdTetX_l-9tfQfKSFyudCU8sq4i4U8mE4Gv8I-5-TWqGeSukprMu1gHyIc/s1600/swapping.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="266" data-original-width="372" height="228" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-88xnjHk5hPnX_wrH-ZQn_M90p8qbAzFon3VoAlxdL_J3mvEO96UTlWP0QDIg-aXtDnFxjAeFBAIJaxHFzTdTetX_l-9tfQfKSFyudCU8sq4i4U8mE4Gv8I-5-TWqGeSukprMu1gHyIc/s320/swapping.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">You probably didn't need a diagram for how to swap something.</td></tr>
</tbody></table>
This variation was pretty simple: As in the classic puzzle you can move the smallest disk to any other peg. But the other disks can only be moved by swapping them with the next-smallest disk (and only when the two swapped disks are at the tops of their stacks). This variation was introduced in 1944, and as far as Professor Stockmeyer could determine no algorithmic solution had been published (and he has <a href="http://www.cs.wm.edu/~pkstoc/biblio2.pdf" target="_blank">looked around pretty thoroughly</a>). You can try it out at <a href="https://sbj42.github.io/projects/tower-of-hanoi/dist/" target="_blank">this demo site I made</a>, by choosing the variation called "Swapping".<br />
<br style="clear: both;" />
I hadn't thought about this for years, and then I go and watch two <a href="https://www.3blue1brown.com/" target="_blank">3blue1brown</a> videos about the Tower (<a href="https://www.youtube.com/watch?v=2SUvWfNJSsM" target="_blank">part 1</a> and <a href="https://www.youtube.com/watch?v=bdMfjfT0lKk" target="_blank">part 2</a>) and the whole thing comes trickling back.<br />
<br />
We were given a challenge: if we could produce a correct algorithm to solve this variation, then the professor would put our names on the paper he was going to publish. It wasn't easy, but after some days we managed to find the solution. The rest, as they say, is in the International Journal of Computer Mathematics.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyt-sh-_W5pJfBx_p_fjhxEZ5aODseRVKPgSuMRny8se4ORrri1Ng2a38yYrGmw_8HQlFzcZeJX0QU1hVlCFp21cuKU49yv3McBIS1_5mJ5gzXGb4A3nXYisbg-E6-AjRYZ4DI2OopinM/s1600/classic-algo.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="85" data-original-width="253" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyt-sh-_W5pJfBx_p_fjhxEZ5aODseRVKPgSuMRny8se4ORrri1Ng2a38yYrGmw_8HQlFzcZeJX0QU1hVlCFp21cuKU49yv3McBIS1_5mJ5gzXGb4A3nXYisbg-E6-AjRYZ4DI2OopinM/s1600/classic-algo.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Classic Tower of Hanoi solution</td></tr>
</tbody></table>
The classic Tower has a simple recursive solution: Consider the biggest disk. You want to get it from peg A to peg C, so first move everything else off of it and onto peg B. Now you can move the big one to C, then move everything else from B to C. The "move everything else" steps are where the recursion comes in, because doing that is just solving the puzzle again but with a slightly smaller tower.<br />
<br />
Here's a notation that will help: We'll list the positions of the disks from biggest to smallest, in a single string. So we start out at "AAAA..." and we want to end up at "CCCC...". The classic solution is to first go from AAAA... to ABBB... (by recursion), then to CBBB..., then to CCCC....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE_2Y2LdwJ14PQ_fZ6RHH1DmB_BEM3VpVIujvVLEeb7kEybsEZ0jYSkQ2H5VXPacUMw8HqIi-AV2Lyyy1Y-W0imKZJOqXhBiLqVv1RW1g2m-Zcw6uKJGxSFaWAVeVx-gMJByJq3oi-AEA/s1600/AAAAA.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="147" data-original-width="372" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE_2Y2LdwJ14PQ_fZ6RHH1DmB_BEM3VpVIujvVLEeb7kEybsEZ0jYSkQ2H5VXPacUMw8HqIi-AV2Lyyy1Y-W0imKZJOqXhBiLqVv1RW1g2m-Zcw6uKJGxSFaWAVeVx-gMJByJq3oi-AEA/s200/AAAAA.png" width="200" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgETq0u5owU1hgAPrZbodZcyxSYO9eZkkTVYJG40oPqWdgsQXzsRXu8Lqka9-dyaiHKXdenhyphenhypheno8V04NzWBixI-oUIJk2D2rTGzXuCmSpaRPj0-KNynNAGSqn5X8vePDmoifkgCUyWyLDRQ/s1600/ABBBB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="147" data-original-width="372" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgETq0u5owU1hgAPrZbodZcyxSYO9eZkkTVYJG40oPqWdgsQXzsRXu8Lqka9-dyaiHKXdenhyphenhypheno8V04NzWBixI-oUIJk2D2rTGzXuCmSpaRPj0-KNynNAGSqn5X8vePDmoifkgCUyWyLDRQ/s200/ABBBB.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE1xxq6-DBMLRdmeMEDvVpGTd-IWYzAjVTK7jEX-3uAz7KsTFszQc_gkB6qmH_hxjsidK81oCmsfSB_sMmhLKXaoTNsaIRV3tAllX1Ou6nvsg_pOBA2qTj2lkiDTi__ivDhBXmV9v7HgE/s1600/CBBBB.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="147" data-original-width="372" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgE1xxq6-DBMLRdmeMEDvVpGTd-IWYzAjVTK7jEX-3uAz7KsTFszQc_gkB6qmH_hxjsidK81oCmsfSB_sMmhLKXaoTNsaIRV3tAllX1Ou6nvsg_pOBA2qTj2lkiDTi__ivDhBXmV9v7HgE/s200/CBBBB.png" width="200" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrFRv_joRX_Q_PWMBReRq3E_IcyjasCjgXrPe3CE96Np5CmNQ08k4TEFElxq_NBf4lz7xI2dY3D8bu8l-IUyUHf464ijQRhi13AV1L0Ml2VrKADl5jSBRyDZdhWhY7JhFigHGjKWC2_8U/s1600/CCCCC.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="147" data-original-width="372" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrFRv_joRX_Q_PWMBReRq3E_IcyjasCjgXrPe3CE96Np5CmNQ08k4TEFElxq_NBf4lz7xI2dY3D8bu8l-IUyUHf464ijQRhi13AV1L0Ml2VrKADl5jSBRyDZdhWhY7JhFigHGjKWC2_8U/s200/CCCCC.png" width="200" /></a></div>
<br />
<br />
<br />
There's another variation, mentioned in the 3blue1brown videos, where you can only move disks to the right, except that you can also move a disk from the last peg to the first. This might be called the Cyclic Tower of Hanoi, and it has a solution that's different from, but similar to, the classic Tower. The difference is that here you can't just move the big disk from A to C. It has to go to B first. So you move everything else to C, move the big disk to B, move everything else to A, move the big disk to C, then move everything else to C. This involves "move everything else" three times, so overall it takes more moves. Using our notation, this means we go from AAAA... to ACCC... to BCCC... to BAAA... to CAAA... to CCCC....<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8by0zTs6M-iM0leT67tV36OiGx8b1Kt6Ch4Id-2rXrOs2przjGKXHXxdDcyxNS8ZxHZZQYJ2DPNpCzUIUG_YTWiOU963PSHDirNcsrkaORkEMAEmSR41Ab6aYdPebskj_V-PvO-611eE/s1600/solve.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="764" height="85" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8by0zTs6M-iM0leT67tV36OiGx8b1Kt6Ch4Id-2rXrOs2przjGKXHXxdDcyxNS8ZxHZZQYJ2DPNpCzUIUG_YTWiOU963PSHDirNcsrkaORkEMAEmSR41Ab6aYdPebskj_V-PvO-611eE/s400/solve.png" width="400" /></a></div>
Our variation involved swapping, and was a little more complicated. Suppose we're going to implement a function "solve", which moves a tower from A to C. To move the big disk to C you can't just get "everything else" out of the way. You need the second-biggest disk to be on C, for the swap to work. That is, we need to get from AAAA... to ACBB.... Let's ignore how to do that for a moment, and just say we have a function that does that, called "unfold". Basically the function "unfold" takes a stack "AAAA..." and moves to "CBBB...", where the biggest disk of that stack is on one peg, and the rest of that stack is on another. Suppose we also have the reverse function, "fold".<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh62RZ0Gxkaplzs9pi86_K1ZVqJGuhFNXtP3JTYWtHTfi1BmpVL7oKqJ20th2LHNX8Y5TsmOw9B_Rn59tSVV3ZF9bSC1RaJC9xYvA9TvWiBTGqyUl1nJ9z8EFxXz6PK0ID7x5s_NGwa-kc/s1600/unfold.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="764" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh62RZ0Gxkaplzs9pi86_K1ZVqJGuhFNXtP3JTYWtHTfi1BmpVL7oKqJ20th2LHNX8Y5TsmOw9B_Rn59tSVV3ZF9bSC1RaJC9xYvA9TvWiBTGqyUl1nJ9z8EFxXz6PK0ID7x5s_NGwa-kc/s400/unfold.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHc7C4PyzHe21CXrQ2INu31lGnuaZyfecUge9V55gxUlOaw8E2swh0GMPLViYAVYdieguPS2ne_3LuURIfA-RiVXwHix7hB_arOH5YOlGh6Yi5PCgVwsB7PnrFdpiG2wzcBesxYB_JcA0/s1600/fold.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="764" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHc7C4PyzHe21CXrQ2INu31lGnuaZyfecUge9V55gxUlOaw8E2swh0GMPLViYAVYdieguPS2ne_3LuURIfA-RiVXwHix7hB_arOH5YOlGh6Yi5PCgVwsB7PnrFdpiG2wzcBesxYB_JcA0/s400/fold.png" width="400" /></a></div>
<br />
So the overall solution function, "solve", can work by first calling "unfold" to set up the big-disk swap (ACBB...), then doing that swap (CABB...), then calling "fold" to tidy everything up (CCCC...). This is a good start, but unlike with the classic and cyclic variations, our "unfold" function isn't just a smaller version of the overall solution. We still need to define it.<br />
<br />
To implement "unfold", how do we get from AAAA... to CBBB...? As before we've got to move the big disk from A to C. So we start by going from AAAA... to ACBB..., then swap to CABB..., then somehow get from CABB... to CBBB.... The first part is easy, we call "unfold" on the sub-tower. This last part is new, though. Now we need some algorithm that can insert a disk underneath a stack. That is, it needs to get from something like ABBB... to BBBB.... This requires another function, let's call that "insert". We'll also need "extract", the inverse of this function (so we can implement "fold").<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzOooXGSnykinzSREn7Y-_i-N-Myh-cl2hZHsDurvmJTQiA2lvp3BAzw8skvzv8b_Q5mpZzjmjqgSAXWulmMB8ErTsdyzQLy-63pYM4iS3NZtXjPsOSL03xvM6-QgZBt6N-oq1uBBAsnE/s1600/insert.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="764" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzOooXGSnykinzSREn7Y-_i-N-Myh-cl2hZHsDurvmJTQiA2lvp3BAzw8skvzv8b_Q5mpZzjmjqgSAXWulmMB8ErTsdyzQLy-63pYM4iS3NZtXjPsOSL03xvM6-QgZBt6N-oq1uBBAsnE/s400/insert.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDlWk_DSwSxRxGYtw6Xh5BwgKH-4GpCOTXDQowXHguY3YGr4qVqwc3yIzSpQRhSQ9iI0WetaM-V09m5Bv3jgIT0uIu1DPL1a63u81L7F_HamUUlHCrWyasroGoqAuElJ_Ftr6Q58Ysq3s/s1600/extract.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="167" data-original-width="764" height="86" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDlWk_DSwSxRxGYtw6Xh5BwgKH-4GpCOTXDQowXHguY3YGr4qVqwc3yIzSpQRhSQ9iI0WetaM-V09m5Bv3jgIT0uIu1DPL1a63u81L7F_HamUUlHCrWyasroGoqAuElJ_Ftr6Q58Ysq3s/s400/extract.png" width="400" /></a></div>
<br />
To implement "insert", how do we get from ABBB... to BBBB...? The big disk needs to get from A to B, so we need to set things up for that swap. This means we need to go from ABBB... to ABCC..., swap to BACC..., and then go from BACC... to BBBB.... Finally we have all the tools we need: The first step there is just moving a whole tower (BB... to CC...), which is the "solve" function we're ultimately building. The last step is "fold" (ACC... to BBB...).<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf3wYxnKvaCffuFaf0QKE6tVoSSWxFbgepwwefWTp5HPedT57T6LyrnfB1gkXNQ6JfmxiMlvVYcHkM4aro4jOPL2s_YV3u7k3KNWUwsl0yDv-8kAUTlzxeI8VicJwjpVcrB5CmVys9Xko/s1600/diagram.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="300" data-original-width="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf3wYxnKvaCffuFaf0QKE6tVoSSWxFbgepwwefWTp5HPedT57T6LyrnfB1gkXNQ6JfmxiMlvVYcHkM4aro4jOPL2s_YV3u7k3KNWUwsl0yDv-8kAUTlzxeI8VicJwjpVcrB5CmVys9Xko/s1600/diagram.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">How the five functions call each other.</td></tr>
</tbody></table>
So our solution involves five functions: "solve", "unfold", "fold", "insert", and "extract". The paper rigorously describes why this solution is optimal, but I prefer to simply note that there aren't really any other options. For the implementation of each function, the biggest disk really does need to move to the right place, and it can only get there if every other disk is set up just right. It seems to me that it's optimal because it only makes necessary moves.<br />
<br />
I made a site that demonstrates this and a few other variations of the Tower of Hanoi, at the link below.<br />
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><br /></span></div>
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/tower-of-hanoi/dist/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Tower of Hanoi Variations</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-55054660143341325632017-06-21T22:12:00.001-04:002017-06-21T22:12:31.761-04:00Musical Notes and the Hilbert Curve <table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8D2mB-mXbmGKeLEG4hxmW5lupXfw1Y7RoIvV1_y7Tcj6wUYy6ylUjg0Dwi-XblLM8csFomSm2QYkuoE85tpTi9r7mK2tlTxcTdJxp5wX2vqeaMKtlonbLt1zyFO9Irp2qNvkzT15pnL8/s1600/3blue1brown.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="648" data-original-width="696" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8D2mB-mXbmGKeLEG4hxmW5lupXfw1Y7RoIvV1_y7Tcj6wUYy6ylUjg0Dwi-XblLM8csFomSm2QYkuoE85tpTi9r7mK2tlTxcTdJxp5wX2vqeaMKtlonbLt1zyFO9Irp2qNvkzT15pnL8/s200/3blue1brown.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">3blue1brown logo<br />seriously go watch the videos</td></tr>
</tbody></table>
Grant Sanderson makes a great series of YouTube videos, called <a href="https://www.3blue1brown.com/" target="_blank">3blue1brown</a>, that explore math concepts with animations. <a href="https://www.youtube.com/watch?v=DuiryHHTrjU" target="_blank">This video talks about Hilbert curves</a>, which are continuous fractal curves that completely fill a two-dimensional area. The video uses them for a hypothetical system of encoding a two-dimensional image (as a grid of pixels) into a one-dimensional sound (as a range of frequencies). I'm not sure such a system would work very well as described (to me, <a href="https://www.nature.com/articles/srep15628" target="_blank">this system called "vOICe"</a> seems a bit more practical) but that's not really the point of the video. It's just a setting for talking about the curves, and how a concept like "approaching infinity" can have practical applications.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGeCZuIgRDTqB_B4_ZL0Aefo-LHJolaSDGw6oiYABneclLu53YRQwv0DDqyzOJzgwV3Y0riLAiPfhPE0UjDTWHutvoVBOUVZybk1x3Z7OAJuKK_W5RKHufnAWV-RxSpl6p1qfc4KQOD7U/s1600/hilbert.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="512" data-original-width="512" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGeCZuIgRDTqB_B4_ZL0Aefo-LHJolaSDGw6oiYABneclLu53YRQwv0DDqyzOJzgwV3Y0riLAiPfhPE0UjDTWHutvoVBOUVZybk1x3Z7OAJuKK_W5RKHufnAWV-RxSpl6p1qfc4KQOD7U/s200/hilbert.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Fourth order Hilbert curve<br />would not make a good maze</td></tr>
</tbody></table>
Here, the Hilbert curve is used as a mapping from a 1D space into a 2D space. A useful property of these curves is that when you increase the resolution (or the number of iterations) the mapped 2D location of a given 1D value is not drastically changed. So someone can get used to 0.083 being at the top-center of the image, and it doesn't much matter what resolution is chosen for the curve - it's still basically at the top-center.<br />
<br />
After watching the video, I wondered about the reverse procedure. That is, what kind of images would it take to produce a given sound. In particular, where would the standard musical pitches lie on the curve, and what would chords look like?<br />
<br />
<br />
So I made a little web experiment to find out. I call it <a href="https://sbj42.github.io/projects/hilbert-music/dist/" target="_blank">Hilbert Chords</a>. Here's how it works: I mapped the 1D space onto a single octave, the range of frequencies between middle-C and high-C. It's a logarithmic scale, so the 12 pitches in the chromatic scale (plus one for the repeated C) are spaced evenly apart. You can move your mouse over either the 2D or 1D regions to hear the corresponding pitch and also see where each point maps to in the other space. Also you can click a handful of buttons that play preselected chords and see what the chord would look like.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOQiyYRfkoKf-cVIS1DUh3PFfvJl7ldoMLVfFxWMLKsGJZ-j6xDvoKWgibU_9K-Fu2-4qqU1xPQ_4ZKzo2Eq9zFG8ETxNJygCJtKQCEE9HnC0UpeSyoqirmGB0u799bNoIXz-2yYmux1M/s1600/screenshot.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="561" data-original-width="532" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOQiyYRfkoKf-cVIS1DUh3PFfvJl7ldoMLVfFxWMLKsGJZ-j6xDvoKWgibU_9K-Fu2-4qqU1xPQ_4ZKzo2Eq9zFG8ETxNJygCJtKQCEE9HnC0UpeSyoqirmGB0u799bNoIXz-2yYmux1M/s320/screenshot.png" width="302" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">locations of pitches on the chromatic scale<br />in color.. because, you know..</td></tr>
</tbody></table>
I was surprised, at first, to see that the notes all lie on corners of the four quadrants. After thinking about it a bit, it makes sense. Each quadrant connects to the next one at one of its corners. The pair of corners connecting two quadrants are basically the same pitch. So that leaves three distinctly-pitched corners per quadrant, for a total of twelve (and again there's the final corner for the repeated C).<br />
<br />
When we increase the number of iterations, the curve gets denser, but the points representing the pitches move less and less. As the number of iterations approach infinity, the pitch-points approach specific points on the square. I don't have a complete understanding of the math, but it seems to me that four of them (E, G#, and the two Cs) approach the corners, six of them (C#, F, G, A, and B) approach the centers of the sides, and three of them (D, F#, and A#) approach the center. At sufficiently high resolution, you wouldn't be able to see the difference between some very different chords, and the D augmented chord would look like a single point in the middle.<br />
<br />
Ok, not a good visualization of the chromatic scale. A plain circle is much better. Or even <a href="https://sbj42.github.io/projects/mu-music-library/demo/keyboard-demo/demo/keyboard.html" target="_blank">just a keyboard</a>.<br />
<br />
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><a href="https://sbj42.github.io/projects/hilbert-music/dist/" target="_blank">Hilbert Chords</a></span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-54333960206619870452017-06-13T22:23:00.002-04:002017-06-13T22:23:44.247-04:00Do not Discharge Stink Bombs in AndoverI'm not a lawyer. Let's start with that.<br />
<br />
<div style="text-align: right;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZl5STlBta5b7cV8FjDcYHLp-s0kR5rPoj1No7u3t0-Qmv9SJ88CJ0eG8icGoURMLsIxRIOdWWcbIYyx2Wg7hiJkvo3JFOOLbMamat4inb9SwL74D0p34tZuJUZb7uu_Kah5jdmbC_Nik/s1600/sillystring.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="180" data-original-width="240" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZl5STlBta5b7cV8FjDcYHLp-s0kR5rPoj1No7u3t0-Qmv9SJ88CJ0eG8icGoURMLsIxRIOdWWcbIYyx2Wg7hiJkvo3JFOOLbMamat4inb9SwL74D0p34tZuJUZb7uu_Kah5jdmbC_Nik/s200/sillystring.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">(Los Angeles, not Andover)<br />
credit: <a href="https://www.flickr.com/photos/steveisaacs/313452508" style="font-size: 12.8px;" target="_blank">Steve Isaacs</a></td></tr>
</tbody></table>
But I was, you know, browsing through the <a href="http://www.ecode360.com/AN1471" target="_blank">code of bylaws of Andover, Massachusetts</a>, and a few things jumped out at me. First, the prohibition on certain items of amusement in <a href="http://www.ecode360.com/15620151" target="_blank">Article XII §39(c)</a>:<br />
<blockquote class="tr_bq" style="text-align: left;">
<span style="font-size: 18px; text-align: justify;"><span style="font-family: "times" , "times new roman" , serif;">The sale, distribution and discharge of stink bombs, smoke bombs and aerosol silly string products within the Town of Andover are prohibited.</span></span></blockquote>
This is apparently not an uncommon kind of ban. Silly String is also banned in Middleborough, among other towns, and notably in Los Angeles, though <a href="http://www.motherjones.com/politics/2014/10/silly-string-hollywood-halloween-ban/" target="_blank">only on Halloween</a>. Stink bombs are <a href="https://library.municode.com/ok/oklahoma_city/codes/code_of_ordinances?nodeId=OKMUCO2010_CH30MIPROF_ARTXIIOTOF_S30-371PRSTBO" target="_blank">banned in Oklahoma City</a>. And there's <a href="https://legiscan.com/NH/text/HB100/id/1441942" target="_blank">New Hampshire 2017 House Bill 100</a>, which would provide an exemption<i> </i>for "toy smoke devices" on the apparently more general <a href="http://www.gencourt.state.nh.us/rsa/html/LXII/644/644-16-b.htm" target="_blank">prohibition of smoke bombs</a>. I guess <i>serious</i> smoke bombs would still be banned.<br />
<br />
I don't know of any other town that has banned all three. But I didn't do the most thorough of searches. Nor did I, in ten minutes of googling, see any codes that provided an exception like that of §39(d):<br />
<blockquote class="tr_bq">
<span style="font-size: 18px; text-align: justify;"><span style="font-family: "times" , "times new roman" , serif;">This by-law shall not apply to public safety personnel in the conduct of their duties.</span></span></blockquote>
<div style="text-align: left;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEefqKDJtLgHpKRXJC-08G39BUM28ceXoKSSwZC3EMoucayTd-scLBSadwhCJBM86QRmCorWxJ_qAgRTWEMge1aRpa1_pX5iwiog51co7eHDOH_-jnf-dmcNrY2YOponXOqSZFmC4xtiU/s1600/police.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="543" data-original-width="725" height="149" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEefqKDJtLgHpKRXJC-08G39BUM28ceXoKSSwZC3EMoucayTd-scLBSadwhCJBM86QRmCorWxJ_qAgRTWEMge1aRpa1_pX5iwiog51co7eHDOH_-jnf-dmcNrY2YOponXOqSZFmC4xtiU/s200/police.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">(Don't worry, this was photoshopped)<br />
credit: <a href="https://www.reddit.com/r/photoshopbattles/comments/37xxm3/psbattle_police_officer_sitting_on_the_ground/crqt4mp/" style="font-size: 12.8px;" target="_blank">What_No_Cookie</a></td></tr>
</tbody></table>
<span style="font-family: inherit;">So it's fine for a police officer to use silly string, as long as it's part of their job. I don't know how often that comes up. Maybe if they're demonstrating things you're not allowed to do? "Hey kids, see this stuff I'm spraying everywhere? This is Silly String. Looks like fun, huh? Well, you can't do this! Also, drugs."</span><br />
<span style="font-family: inherit;"></span><br />
<span style="font-family: inherit;"></span>
<span style="font-family: inherit;">§39 was enacted in 1998, after the town "experienced severe problems" with these sorts of devices. It's hard for me to imagine how tough it must have been back then; I wasn't living in Andover at the time, so who am I to judge? One thing's for sure though, I haven't had any severe silly string problems since I've moved in. So I guess the ban is working!</span><br />
<span style="font-family: inherit;"></span><br />
<span style="font-family: inherit;"></span>
<span style="font-family: inherit;">The rest of Andover's bylaws are, for the most part, as interesting as you would expect bylaws to be.</span><br />
<span style="font-family: inherit;"></span><br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT3aPjMdmlMEcDw28ljlBKAw03LIuSWN7QScGjZNgbJNQ-3H8YpP-Po5hfxw4eKglEUEGVZtP6U2duEsrRRAiTIgi7Kjer1wopEVtFSCuQ8TYktKu8gZBvDlT4skE93LuB312Lfv5QYRY/s1600/bowling.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1600" data-original-width="1600" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgT3aPjMdmlMEcDw28ljlBKAw03LIuSWN7QScGjZNgbJNQ-3H8YpP-Po5hfxw4eKglEUEGVZtP6U2duEsrRRAiTIgi7Kjer1wopEVtFSCuQ8TYktKu8gZBvDlT4skE93LuB312Lfv5QYRY/s200/bowling.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Awww yeah, candlepin.</td></tr>
</tbody></table>
<span style="font-family: inherit;"></span><span style="font-family: inherit;"><a href="http://www.ecode360.com/15619705" target="_blank">Article XI §4</a>, which allows the Selectmen to "</span>grant licenses for the operation of bowling alleys on the Lord's Day between the hours of 1:00 and 11:00 p.m.". I think that bit refers to "[Massachusetts] General Laws, Chapter 136, Section 4B", which <a href="https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXX/Chapter136/Section4" target="_blank">doesn't seem to exist</a> at the moment. But it's probably talking about something in the <a href="https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXX/Chapter136" target="_blank">Common Day of Rest Law</a>, which goes on and on about things you're not generally allowed to do on Sunday in Massachusetts, at least not without a license: such as dancing in exchange for money, offering to watch people dance in exchange for money, and just generally conducting business. I'm not a lawyer, and that whole law seems ridiculous, so I'm probably not reading it right. Anyway, it's good to know that if there were any bowling alleys in Andover (there aren't) then they could operate on Sunday if they got the appropriate license.<br />
<br />
What you can't get a license for, according to <a href="http://www.ecode360.com/15619731" target="_blank">Article XI §8(a)</a>, is an "automatic amusement device [kept at a business and operated for hire] which presents a risk of misuse as a gaming device". I'm not sure about "misuse as a gaming device". Is this indicating that gaming devices in general are bad, and we wouldn't want anyone using a non-gaming amusement device for gaming? Maybe the misuse is bad, but only the kind of misuse that can happen with gaming devices. <a href="https://malegislature.gov/Laws/GeneralLaws/PartI/TitleXX/Chapter140/Section177A" target="_blank">MGL Chapter 140 Section 177A</a> seems to indicate that pinball machines are "automatic amusement devices", but I would assume using it as a gaming device wouldn't be misuse.<br />
<br />
Somehow I think they had slot machines in mind here, but they also helpfully list characteristics associated with risky amusement devices. If it has any of those characteristics, then no license for you. For instance, any device that "involves matching, random numbers, patterns or cards". Or one that "accumulates more than 26 plays" (26 seems like a strange threshold). Look out for any device that's "equipped with a "knock off" switch, button or similar device", those also carry a risk of misuse, somehow.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6L5p358z842IxV8JR1pmbcE2TevmjN60Paa1EvsmEkdp7E1COd9f788A2mVtH04zl0gBeNsfua4DIWuFuK6XtxM1BIH_9itK15mmMqPwCSIXvsOh91ketWomzdTGGi88eQaynH1wXZFE/s1600/goat.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="434" data-original-width="640" height="135" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6L5p358z842IxV8JR1pmbcE2TevmjN60Paa1EvsmEkdp7E1COd9f788A2mVtH04zl0gBeNsfua4DIWuFuK6XtxM1BIH_9itK15mmMqPwCSIXvsOh91ketWomzdTGGi88eQaynH1wXZFE/s200/goat.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Probably needs a rabies shot, though.</td></tr>
</tbody></table>
There is the bit where it says one may keep or board pet cows (part of <a href="http://www.ecode360.com/15244353" target="_blank">Article VII §3.2.1.2</a>), which I guess is cool, and let me to discover in the Board of Health regulations that the "Keeping of Goats Permit" is only $30.<br />
<br />
So in summary, I'm not a lawyer, but I think you can ride your goat to the bowling alley on Sunday. And you can bring your Stink Bombs but you can't use them. And, please, play pinball only in moderation.James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-4811315263227127702017-06-01T22:07:00.000-04:002017-06-01T22:07:05.784-04:00BlockFractal: generator of blocky fractal-like shapes<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAGXreJ8pq2iu4tGDPZ6IlLfcfmL2ZXktlfokkeaeT8Lm5ISZUymdo0ses6VdEsPc5e6XaGbA_pRc__Nfx2Yji3AQErKdpeSuptMPKNtGxpTyP8NepmwQWNws-fdnFyoFZ4qZ5FERCAmA/s1600/North+Osia.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="272" data-original-width="252" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAGXreJ8pq2iu4tGDPZ6IlLfcfmL2ZXktlfokkeaeT8Lm5ISZUymdo0ses6VdEsPc5e6XaGbA_pRc__Nfx2Yji3AQErKdpeSuptMPKNtGxpTyP8NepmwQWNws-fdnFyoFZ4qZ5FERCAmA/s200/North+Osia.png" width="185" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The frabjous isle of <a href="https://sbj42.github.io/projects/block-fractal-demo/www/#North%20Osia" target="_blank">North Osia</a></td></tr>
</tbody></table>
<a href="https://sbj42.github.io/projects/block-fractal-demo/www/" target="_blank">BlockFractal</a> is an algorithm that generates blocky fractal-like shapes. The idea is to make something that would be suitable for a continent, island, or lake, in a procedurally generated 2d tile-based game. The demo page shows the generated shape with a Google-Maps-like interface, where you can zoom and pan around.<br />
<br />
First it generates a random place name, like <a href="https://sbj42.github.io/projects/block-fractal-demo/www/#New%20Gharnia" target="_blank">New Gharnia</a>, <a href="https://sbj42.github.io/projects/block-fractal-demo/www/#Superior%20Iukauton" target="_blank">Superior Iukauton</a>, or <a href="https://sbj42.github.io/projects/block-fractal-demo/www/#Twuq'eros" target="_blank">Twuq'eros</a>. That name is used as a random seed to generate a random fractal. You can enter your own place name, and you can control two properties of the fractal generation.<br />
<br />
One is "iterations". This sets how many times the BlockFractal algorithm is run on the initial seed shape. The initial shape is a simple square, but each iteration adds finer and finer random detail to it. Drag the iterations slider around to see how the shape was built.<br />
<br />
The other is "variation". This basically affects the craziness of the shape. Low variation produces a simpler but blockier shape. High variation produces a shape with many more twists and turns. Somewhere in the middle seems to be pretty good for continent-looking shapes.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKhul0yxNAKtTFXi3nzqA9q0_xjVmGOV6u_pAoKQeAPBchYpKywManUgcmjtczzpr1f8BQlFkfrF7xiWVJFvYkIUYdb9p6woXDlWc5SLIoenBtDzydczT2WHFbtqRPreZvIwGSGJ4Smjs/s1600/walkthrough1.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="255" data-original-width="255" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKhul0yxNAKtTFXi3nzqA9q0_xjVmGOV6u_pAoKQeAPBchYpKywManUgcmjtczzpr1f8BQlFkfrF7xiWVJFvYkIUYdb9p6woXDlWc5SLIoenBtDzydczT2WHFbtqRPreZvIwGSGJ4Smjs/s200/walkthrough1.png" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Seed square</td></tr>
</tbody></table>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi85jnmFPvVpbCq9YZDUsZ8BBQ8st5xu7dgxZf340on8TnWVic2fwXx_k62PEMxE1d5IThtu3ot-wPKX3RjWJTpzPyCL-DlcSS9ZjfyYsvgwgmMY0NqL8SFzjieIIuR1VVnJp2ZGtrpEZw/s1600/walkthrough10.png" imageanchor="1" style="margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="255" data-original-width="255" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi85jnmFPvVpbCq9YZDUsZ8BBQ8st5xu7dgxZf340on8TnWVic2fwXx_k62PEMxE1d5IThtu3ot-wPKX3RjWJTpzPyCL-DlcSS9ZjfyYsvgwgmMY0NqL8SFzjieIIuR1VVnJp2ZGtrpEZw/s200/walkthrough10.png" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Iteration in progress</td></tr>
</tbody></table>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic8_LZj7wv3FoeK8Uu336J-sKUy300F9F62Q_5bv2vRctv-jS7AlsIbazktRaTGYWZz9CilAqdKbqup4sYY3wq-taXAQotsxXKBEzJpfsgmH8cP0aZHMWPkV91V02_zfGtS6ui_w7kRz4/s1600/walkthrough13.png" imageanchor="1" style="margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="255" data-original-width="255" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic8_LZj7wv3FoeK8Uu336J-sKUy300F9F62Q_5bv2vRctv-jS7AlsIbazktRaTGYWZz9CilAqdKbqup4sYY3wq-taXAQotsxXKBEzJpfsgmH8cP0aZHMWPkV91V02_zfGtS6ui_w7kRz4/s200/walkthrough13.png" width="150" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">New shape after first iteration</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
There's a thorough description of the algorithm at <a href="https://github.com/sbj42/block-fractal/wiki/Algorithm-Overview" target="_blank">the Algorithm Overview page</a> on the github site, but basically it goes like this: Take the initial seed shape (a 2x2 square in the first of the three example images above), double it, and then for each unit edge make a random decision to leave it be or to move it "inward" or "outward". That produces a new shape which is more complicated, and around twice as big, as the last one.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIsQIuhW2HE2hVC62cVSdwtp-1vn841ZY3VY9mhBL9K54SWLP2SbFXZtbxPCwnqmLtDTW5bmTdhQMOMNZ0NvG-fNzHEiGBkkGebjS7AixaJrt4i3H6sJEy_iisYOvmfAULAYnFMH-OEHI/s1600/Yixonia.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="260" data-original-width="257" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIsQIuhW2HE2hVC62cVSdwtp-1vn841ZY3VY9mhBL9K54SWLP2SbFXZtbxPCwnqmLtDTW5bmTdhQMOMNZ0NvG-fNzHEiGBkkGebjS7AixaJrt4i3H6sJEy_iisYOvmfAULAYnFMH-OEHI/s200/Yixonia.png" width="197" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The frumious land of <a href="https://sbj42.github.io/projects/block-fractal-demo/www/#Yixonia" target="_blank">Yixonia</a></td></tr>
</tbody></table>
Repeat this algorithm as many times as you like, though it does take twice as much memory with each iteration, so to avoid memory issues I stopped the demo at 9 iterations.<br />
<br />
There are a few edge cases to consider, for instance I don't want the path to intersect itself. If you're curious the Algorithm Overview page has more details.<br />
<br />
The resulting shapes make me think of <a href="https://en.wikipedia.org/wiki/Koch_snowflake" target="_blank">Koch snowflakes</a> and <a href="https://imagej.nih.gov/ij/plugins/fractal-generator/index.html" target="_blank">quadric fractals</a>. Also, check out <a href="http://www.brainfillingcurves.com/" target="_blank">Brainfilling Curves</a>, for some similar and amazing shapes.<br />
<br />
As you play with the demo, imagine if this were the high-level map of a top-down RPG or roguelike game. I like the idea of scattering some different biomes and rivers around, randomly placing some cities and towns, and maybe using some <a href="https://en.wikipedia.org/wiki/Delaunay_triangulation" target="_blank">Delaunay triangulation</a> to connect them with roads.<br />
<br />
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/block-fractal-demo/www/" target="_blank">BlockFractal (demo)</a></span></div>
<div style="text-align: center;">
<a href="https://github.com/sbj42/block-fractal" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">(github repo)</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-40563037247770236932017-05-28T21:19:00.002-04:002017-05-31T00:13:05.549-04:00WarpField: portal-casting field-of-view algorithm<div class="separator" style="clear: both; text-align: left;">
Here's <a href="https://sbj42.github.io/projects/warp-field-demo/www/" target="_blank">WarpField</a>, a "portal-casting" field-of-view algorithm. This goes beyond the usual field-of-view mechanism by adding support for portals. A portal basically connects an edge between tiles in one map with an edge in a (possibly different) map. WarpField computes the field of view through the portal, and also returns what the player can see at each location. This could be useful for:</div>
<div class="separator" style="clear: both;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNvrIa2GpWP3aerbuCa_lcKZrcKJc-W3upKGBK22bq_04z3GAdcHcV_RsHtKyT2jbGRC2yOe8N_h2-509iaJRhpHWbCBUgqJ4tIRmZDZFhboxGCGRh_cEhKoON6t40tAs9WfOpSprMbXM/s1600/fov-usage-example1.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="236" data-original-width="363" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNvrIa2GpWP3aerbuCa_lcKZrcKJc-W3upKGBK22bq_04z3GAdcHcV_RsHtKyT2jbGRC2yOe8N_h2-509iaJRhpHWbCBUgqJ4tIRmZDZFhboxGCGRh_cEhKoON6t40tAs9WfOpSprMbXM/s1600/fov-usage-example1.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Going up (or down) stairs.</td></tr>
</tbody></table>
<ul>
<li>Continuous overworld movement from zone to zone</li>
<li>Over/under pathways, such as bridges and tunnels</li>
<li>Smooth transitions of elevation changes, like stairs</li>
<li>Crazy magical effects, like portals to alternate dimensions.</li>
</ul>
In a previous post, I discussed <a href="http://arbitraryclark.blogspot.com/2017/05/wallyfov-shadow-casting-field-of-view.html" target="_blank">WallyFOV</a>, my implementation of a 2d grid-based field-of-view algorithm using shadow-casting. For games that don't need portals, that algorithm is a better choice, since it's simpler and faster. Unlike many field-of-view implementations, WallyFOV supports walls, which is a prerequisite for adding portal support.<br />
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
"Portal-casting" is basically an extension of shadow-casting. Shadow-casting tracks contiguous ranges of angles of rays that are visible from the player. When a wall is encountered, we simply cut that range of angles out (causing a shadow). For portals, instead of cutting that range out, we split it off into a new, separate range. For each range we track which map the range "sees", and the relative tile offset into that map.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXiWMKKGprzcuTy7F9hk7-fwFwnJvLiisvITRjnqx8-QEY-4NmtasqbhqUMyovAgTTfty2SVireH_QVGpxy17ZNDh5Vhh3g5G55nB7Ngllmfa2dn0xvRrBYiBafm6B_lQ9kHsXzdzuY0s/s1600/fov-usage-example2.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" data-original-height="236" data-original-width="363" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXiWMKKGprzcuTy7F9hk7-fwFwnJvLiisvITRjnqx8-QEY-4NmtasqbhqUMyovAgTTfty2SVireH_QVGpxy17ZNDh5Vhh3g5G55nB7Ngllmfa2dn0xvRrBYiBafm6B_lQ9kHsXzdzuY0s/s1600/fov-usage-example2.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Entering a tunnel.</td></tr>
</tbody></table>
For each location that isn't completely in a shadow, we now have to decide which map we're seeing at that location, and which tile in that map goes there. This can be tricky, because we might see the location more than one way at a time. Maybe some rays reach the location after passing through a warp to map A, while others reach it after a warp to map B, and some reach it without passing through any warps at all. In a 2D tile-based game we don't really want to show slices of a tile, we want to just pick one to show. To resolve this ambiguity, WarpField has an order of preference: rays closest to the center are preferred, followed by rays that go through fewer warps, and finally ties are broken by the map with the lowest "id".<br />
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
There are a lot of edge cases to consider, and most of them are touched upon in the <a href="https://github.com/sbj42/warp-field/wiki/Algorithm-Overview" target="_blank">algorithm overview</a>. I did a lot of testing and benchmarking, and I'm pretty sure it works according to spec, but there are some asymmetries that I'd like to remove (due to the order in which locations are visited). I think I can clean that up by switching to fractional math instead of floating-point math. Nevertheless, it's fairly robust, you can throw crazy cases at it like warps into the same map (such that the player can see themself through it), and rays that pass through many warps at once.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both;">
I think this could add a lot of potential to roguelike games.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/warp-field-demo/www/" target="_blank">WarpField (demo)</a></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://github.com/sbj42/warp-field" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">(github repo)</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-12726009809445391042017-05-15T22:07:00.004-04:002017-05-15T22:07:59.974-04:00WallyFOV: Shadow-casting field-of-view implementation with walls<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7YxulK_vptYWGPhYxHJmxnWXZdDc5ZIx8XGhAJwe7J0qocpCoHskJTFF0xPMCIzXBJ8ZHw2iXqLDus5T1DV9ADma22XRY-1kxbZrL8bHA3WYJ2LSzMEGYw9YB80kVyne_0CPUSH_dDUw/s1600/example1.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7YxulK_vptYWGPhYxHJmxnWXZdDc5ZIx8XGhAJwe7J0qocpCoHskJTFF0xPMCIzXBJ8ZHw2iXqLDus5T1DV9ADma22XRY-1kxbZrL8bHA3WYJ2LSzMEGYw9YB80kVyne_0CPUSH_dDUw/s1600/example1.png" /></a></div>
I've been working on a field-of-view algorithm for 2d tile-based games (e.g. roguelikes). There's a particular killer feature I mean to include in the algorithm, which I won't reveal in this post, but I decided to go ahead and release the slightly simpler version because I think it might be useful on its own.<br />
<br />
It's called <a href="https://github.com/sbj42/wally-fov" target="_blank">WallyFOV</a>. It's basically an implementation of <a href="http://www.roguebasin.com/index.php?title=FOV_using_recursive_shadowcasting" target="_blank">recursive shadowcasting</a> in TypeScript, but with support for walls.<br />
<br />
<a href="https://sbj42.github.io/projects/wally-fov-demo/www/" target="_blank">Go ahead and check out the demo.</a> (and thanks to <a href="http://easystarjs.com/">EasyStar.js</a> for the pathfinding algorithm used in that demo, and from which I kindof copied the demo page style)<br />
<br />
Recursive shadowcasting works by scanning outwards from the player looking for obstructions. When something is found, a shadow (represented by two angles) is added to a shadow list. Overlapping shadows are merged to keep the algorithm running quickly.<br />
<br />
In this particular variant, a tile is considered visible if there exists any line from the center of the player's tile to any point within the target tile. There are some complications around dealing with diagonally-adjacent bodies, which you can read about in the <a href="https://github.com/sbj42/wally-fov/wiki/Algorithm-Overview">algorithm overview</a>, but overall it's a pretty simple algorithm to understand. It's a bit tricky to implement and optimize, however.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPwUzpihZZHU26eg8xB7PbnNcmMwyNyMkUApF_Bu9HQwW9fthsTNzdH_CHzpR3nyok_U4zJC6L-XbNpmME4UTAOqZj5TVay-k9wdg3JVeWhywyIUULNW5AuKE_r14idQ4MCUPk9jglLis/s1600/example4.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPwUzpihZZHU26eg8xB7PbnNcmMwyNyMkUApF_Bu9HQwW9fthsTNzdH_CHzpR3nyok_U4zJC6L-XbNpmME4UTAOqZj5TVay-k9wdg3JVeWhywyIUULNW5AuKE_r14idQ4MCUPk9jglLis/s1600/example4.png" /></a></div>
I spent a little extra time optimizing it to run in <a href="https://developers.google.com/v8/">V8</a> (at least the version of V8 in node.js 7.9). Along the way I learned how to use node's --trace-deopt, and discovered some very unexpected optimization tips, mainly having to do with newer ECMAScript features.<br />
<br />
For instance, this version of V8 doesn't optimize "<a href="http://stackoverflow.com/questions/34595356/what-does-compound-let-const-assignment-mean">compound let/const assignment</a>". Meaning if you declare a variable with <span style="font-family: Courier New, Courier, monospace;">let</span> or <span style="font-family: Courier New, Courier, monospace;">const</span>, and then use something like <span style="font-family: Courier New, Courier, monospace;">X += Y</span>, your function will be deoptimized. Simply using <span style="font-family: Courier New, Courier, monospace;">X = X + Y</span> instead avoids that trap. The general lesson here is that JavaScript is apparently changing too rapidly to rely on the engine optimizing things according to your intuition. This is probably true with any language, to a certain extent. But, come on, <span style="font-family: Courier New, Courier, monospace;">X = X + Y</span>?<br />
<br />
<div style="text-align: center;">
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><a href="https://sbj42.github.io/projects/wally-fov-demo/www/">WallyFOV (demo)</a></span></div>
<div style="text-align: center;">
<a href="https://github.com/sbj42/wally-fov"><span style="font-family: Courier New, Courier, monospace;">(github repo)</span></a></div>
<br />James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-85703072712625722072017-01-27T21:06:00.003-05:002017-01-27T21:10:15.322-05:00Two Songs Arranged for Solo Piano<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaUrIt8TtZRw6wxAcUGlqNYpAzI6bFb9JtQVM0etBtbW2QFuPS0C29TE7Snr-Q2890Em4VKFa2bisAEr5iFINjZvUsfv85GrKl_F8omr_ZrWuaPCPGLgNWGPeAwZmCOAcKEXn2CMf0MYo/s1600/coming-up-for-air-sp.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaUrIt8TtZRw6wxAcUGlqNYpAzI6bFb9JtQVM0etBtbW2QFuPS0C29TE7Snr-Q2890Em4VKFa2bisAEr5iFINjZvUsfv85GrKl_F8omr_ZrWuaPCPGLgNWGPeAwZmCOAcKEXn2CMf0MYo/s200/coming-up-for-air-sp.png" width="200" /></a>I've arranged two of my songs for solo piano, using <a href="https://musescore.org/" target="_blank">MuseScore</a>. I had to make some changes in these arrangements compared to the originals, to remove or combine parts, and to make them physically possible to play. I'm not a great pianist, but I made sure that I could play them through at a stop-and-think-about-the-next-part pace. I think someone more skilled should be able to play them properly.<br />
<br />
If you're that person I'd love to hear the result. You can use the Download link at the MuseScore page for each piece to get printable versions of the scores.<br />
<br />
<div style="text-align: center;">
<span style="font-size: large;"><a href="https://musescore.com/user/15835481/scores/3303261" target="_blank">Coming Up for Air</a> (<a href="https://soundcloud.com/arbitraryclark/coming-up-for-air" target="_blank">original</a>)</span></div>
<br />
The easier of the two, this one involves lots of many-fingered chords in both the right and left hands. It's a mellow, ambient piece, and my most recent.<br />
<br />
<div style="text-align: center;">
<span style="font-size: large;"><a href="https://musescore.com/user/15835481/scores/3303281" target="_blank">Halting Problem</a> (<a href="https://soundcloud.com/arbitraryclark/halting-problem" target="_blank">original</a>)</span></div>
<br />
I made this one for <a href="http://arbitraryclark.blogspot.com/2016/03/hello-robot-quest.html" target="_blank">Robot Quest</a>. At the time I called it "Plugged In", but I've renamed it because this name is much better. A quick, boisterous, staccato piece, this one is very hard for me to play.James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-60159994659454678112016-12-18T21:48:00.003-05:002016-12-18T21:48:57.957-05:00Kendall Stories<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM398VIKoCZ5OKZC1oHJZw8IX5egSf8o8NdOR0CKmGn54rC5bP-YapS5bVN8akoqES2DEGb9vOiDlEqcxqhZOPmBEigKOMeS2PJKoxgG4NspNAV0tVfwZQQymXrYFnvSispxBJSdok8a0/s1600/drawing1.jpg" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhM398VIKoCZ5OKZC1oHJZw8IX5egSf8o8NdOR0CKmGn54rC5bP-YapS5bVN8akoqES2DEGb9vOiDlEqcxqhZOPmBEigKOMeS2PJKoxgG4NspNAV0tVfwZQQymXrYFnvSispxBJSdok8a0/s320/drawing1.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Lala and Sing acting out an argument.</td></tr>
</tbody></table>
Here are <a href="https://sbj42.github.io/projects/kendall-stories/" target="_blank">some web animations</a> I made for my daughter. One day she asked me to cut a hole out of a cardboard box so she could use it as a TV. She then made three "shows", and for each one she drew scenes on several sheets of paper. To present the shows, she would hold the paper up to the hole in the box while the rest of us sat on the other side and watched.<br />
<br />
I thought it would be cute to turn her drawings into SVG images and animate them, and to record her voice and sync it up with the animation. She really got into it, giving me very strict directorial instructions. Part of the challenge for me was to try not to "improve" on her work with my own ideas. I tried to stay as true to her vision for the shows as possible, and I included all of her mistakes (that she didn't catch in post-production and demand that I fix).<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFY8bVR7D8QJf390puQe0c3UlpYBJXsLEmnerBAabv4-gYNr4bdbq7jfexQroybEbzC8N0aRHYegKqE35aosIemvEyDPAi5BxpabdYNaKPpGEsiPhlxbS1Jr0xw9CKGYG5k4T1dm31xPQ/s1600/drawing2.jpg" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="111" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFY8bVR7D8QJf390puQe0c3UlpYBJXsLEmnerBAabv4-gYNr4bdbq7jfexQroybEbzC8N0aRHYegKqE35aosIemvEyDPAi5BxpabdYNaKPpGEsiPhlxbS1Jr0xw9CKGYG5k4T1dm31xPQ/s200/drawing2.jpg" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Not gonna what?</td></tr>
</tbody></table>
<br />
I used <a href="http://greensock.com/gsap" target="_blank">GreenSock GSAP</a>'s TweenMax library for the animations, and <a href="https://howlerjs.com/" target="_blank">howler.js</a> for audio. Both are overpowered for this kind of project, but they saved time that I would have spent dealing with little headaches. For instance you can't trigger HTML5 audio directly on mobile browsers. I should have found a library for preloading and sizing images, because I ended up with some frustrating work making sure image sizes were loaded properly for IE.<br />
<br />
<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/kendall-stories/" target="_blank"><span style="font-family: Courier New, Courier, monospace; font-size: large;">Kendall Stories</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-36012870598760309032016-11-27T21:45:00.000-05:002016-12-08T22:35:25.972-05:00Word Search GeneratorHere's a quickly-slapped-together little project that generates word search puzzles.<br />
<br />
I've been kicking around an idea for a kind of "evil" word search, in which there are tons of letter combinations that almost spell words, but don't. The trick would be to see how distracting the almost-words are, and whether that makes the correctly-spelled words harder to find. That's not what this one is, though. This one is just a regular, non-evil word search generator.<br />
<br />
The core generator code is at <a href="https://github.com/sbj42/word-search-generator" target="_blank">this githib repo</a>, or on npm as <a href="https://www.npmjs.com/package/@sbj42/word-search-generator" target="_blank">@sbj42/word-search-generator</a>.<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/word-search-generator/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Word Search Generator</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-84463385031773404752016-11-20T21:03:00.000-05:002016-12-08T22:35:04.961-05:00Mr. Floppypants<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUOVznzWeYadO6nXb-CEQyTRQ5Wz_PDC1MHpiGrd3orIUc1cE8cCcaRK17PBlHs7b76j3FrcPxjvMU9iey5BAjYxB_ostKL268llhTL9qHr0bGbfcaUXoC-3uZxGlfDjGccjgsepTBi1E/s1600/mr-floppypants.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUOVznzWeYadO6nXb-CEQyTRQ5Wz_PDC1MHpiGrd3orIUc1cE8cCcaRK17PBlHs7b76j3FrcPxjvMU9iey5BAjYxB_ostKL268llhTL9qHr0bGbfcaUXoC-3uZxGlfDjGccjgsepTBi1E/s1600/mr-floppypants.png" /></a>Here's <a href="https://sbj42.github.io/projects/mr-floppypants/" target="_blank">Mr. Floppypants</a>. A simple ragdoll-physics game. Maybe it's about a very lazy person who needs to be forced to go explore and have fun.<br />
<br />
It's based loosely on a "Chrome experiment" called <a href="https://www.chromeexperiments.com/experiment/boingy" target="_blank">Boingy</a>, which the kids really got a kick out of. I think they mostly liked dropping stuff on the poor protagonist, or forcing it to fall down stairs, or getting it twisted up into painful-looking poses. After I played a bunch of <a href="https://manygolf.club/" target="_blank">MANYGOLF</a> and through that learned about the <a href="https://github.com/schteppe/p2.js">p2.js</a> javascript physics engine, I thought I'd make a more complicated version of my own.<br />
<br />
It seems to work in Chome, Firefox, and Edge. It does not work in IE.<br />
<br />
Basically you click and drag to toss the guy around. For more advanced play, if you click on his hand and drag it to something then he'll "grab" the thing. You can use this to carry an item with you to some other part of the world, and to operate the car.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnDs7RKP56BqUvcRaQ13YC2FdG76SWqqdkpv3ZxZSG4XEC_dSt8mykk52NFDP6ZqqR5IqC8rglNFtorGnVd-NwYrxaPteybjnYCwi5q11a_7KYIZhopdPXc1aGytJq8bXmNRJz0GigE9g/s1600/car-jump.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="97" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnDs7RKP56BqUvcRaQ13YC2FdG76SWqqdkpv3ZxZSG4XEC_dSt8mykk52NFDP6ZqqR5IqC8rglNFtorGnVd-NwYrxaPteybjnYCwi5q11a_7KYIZhopdPXc1aGytJq8bXmNRJz0GigE9g/s200/car-jump.png" width="200" /></a></div>
<br />
This game isn't nearly finished yet. I was hoping to make the world a lot larger, and to add other characters to meet and also toss around. If you get any ideas for things I could add to the game, let me know in the comments below.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkxRStfIobm5X8VYNBZqN24138Tx7sNPp1_yJ-lvx3-sJwKnWMX9OOl2qVhc_dvBBraoshO315xb5wJofPNgg2BfQsZ1LGcR2mvWNKPM68jzuD7LJOk7FOMgcKEVs7tINVpzo8pa1G4LA/s1600/swimming.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgkxRStfIobm5X8VYNBZqN24138Tx7sNPp1_yJ-lvx3-sJwKnWMX9OOl2qVhc_dvBBraoshO315xb5wJofPNgg2BfQsZ1LGcR2mvWNKPM68jzuD7LJOk7FOMgcKEVs7tINVpzo8pa1G4LA/s1600/swimming.png" /></a></div>
<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/mr-floppypants/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Mr. Floppypants</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-36040150458273370942016-10-18T20:46:00.000-04:002016-10-18T20:50:42.262-04:00Song: Coming Up for Air<div style="text-align: left;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzPFrVPQcWz1HYv9YEv5vvW9rIVAo0SPqeG-pP2-FIK4yg2AvY74PR09uvfdPEzIa8ErriwOQbp-0gHssLsuThBCzbM6cukv6E5KZtOlOUTwyy0wXz3eqR73lDspZwFFWVHDWrkY5H4Q/s1600/coming-up-for-air.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="100" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCzPFrVPQcWz1HYv9YEv5vvW9rIVAo0SPqeG-pP2-FIK4yg2AvY74PR09uvfdPEzIa8ErriwOQbp-0gHssLsuThBCzbM6cukv6E5KZtOlOUTwyy0wXz3eqR73lDspZwFFWVHDWrkY5H4Q/s200/coming-up-for-air.png" width="100" /></a><span style="font-family: inherit;">I was trying to make some background music for <a href="https://sbj42.github.io/projects/its-eelectric/" target="_blank">It's EELectric!</a>, but I failed. I had various ideas, mostly working around an undersea theme. I originally wanted something like <a href="https://www.youtube.com/watch?v=Jf-vCCbji0w" target="_blank">the "Evan's Room" song</a> from Legend of Zelda: Majora's Mask. (digression: I always associated that song with being underwater because that's the context in which you hear it in the game, but I just learned from <a href="http://zelda.wikia.com/wiki/Zora_Hall" target="_blank">Zeldapedia</a> that it's also the "Game Over" music from the original Legend of Zelda.)</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Anyway after various ideas didn't seem to work, I settled on a sequence of 9th chords. I expanded that into a more complete piece with a solemn, contemplative verse and a hopeful chorus. Then I realized that it didn't at all fit the whimsical theme of the game for which I was writing it, and completely gave up on using it in that context.</span><br />
<span style="font-family: inherit;"><br /></span>
<br />
<span style="font-family: inherit;">Now I don't have anything to attach it to, so here it is by itself:</span><br />
<span style="font-family: inherit;"><br /></span></div>
<iframe frameborder="no" height="166" scrolling="no" src="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/288769529&color=ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false" width="100%"></iframe>
<br />
<div style="text-align: center;">
<a href="https://soundcloud.com/arbitraryclark/coming-up-for-air" target="_blank"><span style="font-family: "arial" , "helvetica" , sans-serif; font-size: large;">Coming Up for Air</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-76150406312620392092016-10-14T22:53:00.002-04:002017-06-15T21:29:55.696-04:00Maze generatorsI'm working on another game for the kids, called <a href="http://arbitraryclark.blogspot.com/2016/11/mr-floppypants.html" target="_blank">"Mr. Floppypants"</a>, of which I'll say no more in this article, except that I'm using <span style="font-family: "courier new" , "courier" , monospace;"><a href="https://nodejs.org/en/" target="_blank">node.js</a></span>, and that for it I required some code to generate a maze. For once not wanting to write my own code for that particular subfeature, I searched around <span style="font-family: "courier new" , "courier" , monospace;"><a href="http://npmjs.org/" target="_blank">npm</a></span> for "maze generator" packages. There were a few.<br />
<br />
<div style="text-align: right;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJS4p19aq6NHa_9BbCJywb6h8t5wvYrq4dnRhRqv8N9zIo5gRrE9AFy6CivvoDYKWgAMMJc0GeFPFLi45brYZ1ESZpYB2GRCa6i1jkKkSFDcIXRXM4gqy0ofeex6WjXzmZ4xpn01Hg6UM/s1600/example30x30.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJS4p19aq6NHa_9BbCJywb6h8t5wvYrq4dnRhRqv8N9zIo5gRrE9AFy6CivvoDYKWgAMMJc0GeFPFLi45brYZ1ESZpYB2GRCa6i1jkKkSFDcIXRXM4gqy0ofeex6WjXzmZ4xpn01Hg6UM/s1600/example30x30.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Watch out for bull-men!</td></tr>
</tbody></table>
I set about looking into each one in turn to see if it would suffice, but I discovered for each one a reason not to use it. One of them simply didn't run, another used Ecmascript 6 (and so wouldn't work in some browsers without being transpiled), one only worked for square mazes, and some (and I admit I was a bit picky) just weren't written to my taste. Maze generation is a fun little project, and it's natural that many people have made their own generators, in varying degrees of quality. Yes, I gave up and wrote my own.<br />
<br />
Here's what I learned about mazes... wait, no. Instead let me point you at some much better sources of information:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguhbHA3oCiK86zLtyDCoUxHddaC_bM34kmZKO8_JTDEmIOETi_7SJ1O8ML_luH_cU3L8NLwY7u1K2I_u0zoxNiiyswgqA67oqXzKV_kh6Z2jjuaEAacuN9k1Q533w1mEmwTmgxjcfKLPI/s1600/gear-maze.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguhbHA3oCiK86zLtyDCoUxHddaC_bM34kmZKO8_JTDEmIOETi_7SJ1O8ML_luH_cU3L8NLwY7u1K2I_u0zoxNiiyswgqA67oqXzKV_kh6Z2jjuaEAacuN9k1Q533w1mEmwTmgxjcfKLPI/s1600/gear-maze.png" /></a><a href="http://weblog.jamisbuck.org/" target="_blank">Jamis Buck</a> writes a mostly-programming blog, and wrote a great series on maze generators in 2010 and 2011. <a href="http://www.jamisbuck.org/presentations/rubyconf2011/index.html" target="_blank">This in-browser presentation</a> sums up the whole maze generation concept very nicely, and <a href="http://weblog.jamisbuck.org/2011/2/7/maze-generation-algorithm-recap.html" target="_blank">this recap of the blog series</a> is a great place to get more details. He describes how to implement a handful of algorithms, and provides in-browser demos so you can watch the algorithm run.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBH6aJlkzhWjAvxXF2u-P-0qtZOS4p3_jZ30BnyZ4xa2tBk4nmgAV8LP1ZDkHwPC8taO1D_rWjBaCRzgEao-xi3y5dOnaPPir-fkTRhdXpOKV8ufqG3fglwNgQbSaBt562RAmuWzOTvDg/s1600/didymus.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBH6aJlkzhWjAvxXF2u-P-0qtZOS4p3_jZ30BnyZ4xa2tBk4nmgAV8LP1ZDkHwPC8taO1D_rWjBaCRzgEao-xi3y5dOnaPPir-fkTRhdXpOKV8ufqG3fglwNgQbSaBt562RAmuWzOTvDg/s1600/didymus.png" /></a><a href="http://www.astrolog.org/home.htm" target="_blank">Walter Pullen</a> has such a variety of interests that I refuse to summarize his site, in one part of it you can find a very thorough maze generator program he wrote, and he has <a href="http://www.astrolog.org/labyrnth/algrithm.htm" target="_blank">written extensively about maze generation</a>. Also I just can't keep myself from mentioning his extensive files about <a href="http://www.astrolog.org/labyrnth/movie.htm" target="_blank">the movie Labyrinth</a>.<br />
<br style="clear: left;" />
Now you know everything about mazes. But just in case you didn't spend hours reading through all that stuff, here are some highlights:<br />
<br />
A maze is basically equivalent to a <a href="https://en.wikipedia.org/wiki/Spanning_tree" target="_blank">spanning tree</a> of a graph based on a rectangular grid. Consequently, algorithms that compute spanning trees can be adapted to make mazes. Kruskal's algorithm is like this, and builds mazes in an seemingly magical way: It connects random adjacent cells, taking care not to create a loop, until a maze basically appears out of the noise.<br />
<br />
Speaking of Kruskal's, the hardest part is quickly deciding whether two adjacent cells are already connected. It can be done with a <a href="https://en.wikipedia.org/wiki/Disjoint-set_data_structure" target="_blank">disjoint-set forest</a>, and the tricks they use to make that fast (union by rank and path compression) are neat.<br />
<br />
One of the simplest algorithms, recursive-backtracking, is my pick for making the best-looking mazes. While generators like Kruskal's tend to make lots of clustered dead-ends, recursive-backtracking prefers meandering passages. It works as basically a randomized depth-first-search of the grid.<br />
<br />
Eller's algorithm can generate the maze one entire row at a time. It works by keeping track of which cells connected to the previous row have already been connected (possibly by an earlier, now forgotten row). On the very last row, it patches up any not-yet-connected regions of the maze. In doing so, it may end up with a long straight passage along the bottom.<br />
<br />
The Aldous-Broder algorithm generates all possible mazes with equal probibility, but you probably don't want to use it because it's very slow. It's interesting to think about though: Start in a random cell and perform the well-named <a href="https://en.wikipedia.org/wiki/Random_walk" target="_blank">"drunkard's walk"</a>, digging a passage when (and only when) you walk into a cell you've never visited before. Assuming you have enough patience, you'll eventually visit every cell and oh hey, there's a maze!<br />
<br />
It's not a generator, but "dead end filler" is an amusing way to solve some mazes without ever visiting cells in the solution path. Find all the dead ends in the maze and fill them in. Then find any new dead ends you created, and fill those in too. Repeat until there are no more dead ends. The remaining unfilled spaces are the solution.<br />
<br />
Back to my project: I implemented three algorithms (recursive-backtracking, modified Prim's, and Kruskal's). I also used this as an opportunity to learn about node and npm, so the algorithm packages also have unit tests, benchmarks, and demos, and I spent some time trying to optimize them for speed and also make them easy to plug in to another project without needing to bring a lot of dependencies along.<br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/maze-generator-demo/" target="_blank">Demos of my maze generator algorithms</a></span></div>
<div style="text-align: center;">
<br />
<span style="color: #0000ee; font-family: "courier new" , "courier" , monospace; font-size: large; text-decoration: underline;">@sbj42/maze-generator npm package</span><br />
<div style="text-align: start;">
<br /></div>
<div style="text-align: start;">
Update (10/15): I added a maze "puzzle" to <a href="https://sbj42.github.io/projects/probably-puzzles/" target="_blank">Probably Puzzles</a>.<br />
Update (6/17): Added a link to <a href="http://arbitraryclark.blogspot.com/2016/11/mr-floppypants.html" target="_blank">Mr. Floppypants</a></div>
</div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-13091399849546724232016-08-27T22:53:00.002-04:002016-08-27T22:53:10.341-04:00Generating Shikaku-like puzzles<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_LrVwcjXZLeQl_efEeNmjCvIJ6G_0EiOBsySc7MBhxlbn3IyOeOOKZWBfJD-JrzrbuKegFiTxNPjdXHT4zCkEp4YeIyPxWP3HGsChzlEGoNBuYkndMBXoYkyxivdYZOyGNzt_iqhi05o/s1600/shikaku.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_LrVwcjXZLeQl_efEeNmjCvIJ6G_0EiOBsySc7MBhxlbn3IyOeOOKZWBfJD-JrzrbuKegFiTxNPjdXHT4zCkEp4YeIyPxWP3HGsChzlEGoNBuYkndMBXoYkyxivdYZOyGNzt_iqhi05o/s1600/shikaku.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A very small<br />
Shikaku<br />
in progress.</td></tr>
</tbody></table>
In <a href="http://arbitraryclark.blogspot.com/2016/08/generating-nonograms.html" target="_blank">my last post</a>, I talked about adding Nonograms to <a href="https://sbj42.github.io/projects/probably-puzzles/demo/4puzzles/" target="_blank">Probably Puzzles</a>. Now let's talk <a href="http://www.nikoli.co.jp/en/puzzles/shikaku.html" target="_blank">Shikaku</a>.<br />
<br />
In Shikaku your task is to divide a grid into rectangular regions, such that every cell is assigned to a region, every region has exactly one number in it, and that number is the area of the region.<br />
<br />
This one turned out to be harder to generate than Hidato, though maybe I haven't found the right algorithm. The first step was to generate a random division of the grid into rectangles (i.e. to generate the solution). I didn't find a good algorithm for that, so I made up a mediocre one:<br />
<ol>
<li>Choose a random cell that isn't yet in a rectangle</li>
<li>Randomly decide on a major axis (horizontal or vertical)</li>
<li>See how far we can stretch from that cell along the major axis (e.g. for horizontal we see how many empty cells there are to the left and right)</li>
<li>Choose a random range from that span, with length no greater than 8</li>
<li>See how far a rectangle of that width can go along the minor axis</li>
<li>Choose a random range from that span, no longer than 8, where the rectangle's area is no bigger than 16</li>
<li>If we were able to pick a rectangle bigger than 1x1, then go back to 1 and repeat until all cells are consumed.</li>
<li>If not, then pick a random rectangle adjacent to the cell and remove it, then go back to 1.</li>
</ol>
That last step was the key to speed. At first I had it just give up and start over if it hit an isolated cell, but it had to do a lot of resetting before it randomly ended up perfectly dividing the grid. The last step allows it to make a small adjustment and try from there, rather than losing all the work it put into the earlier rectangles.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2mLFiTXgkDU5FAaq0hpKNnoUqIqiRGThfLv1meaM0C1W8R8YHAVoR57z-GcCR76g60X2yHyHUjgIdfX6PanFXvHoparwcNaBlJgTtZ1XyimsACG6st7TjoKORMYsIg_farvTco5LU3CU/s1600/shikaku9c.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2mLFiTXgkDU5FAaq0hpKNnoUqIqiRGThfLv1meaM0C1W8R8YHAVoR57z-GcCR76g60X2yHyHUjgIdfX6PanFXvHoparwcNaBlJgTtZ1XyimsACG6st7TjoKORMYsIg_farvTco5LU3CU/s320/shikaku9c.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A grid, divided.</td></tr>
</tbody></table>
This gives us a divided grid. A solution in search of a problem. So now we choose a random cell from each rectangle to use as the hint. Here things get a little tricky. To make sure there is only one unique solution for a given puzzle, we need to go ahead and solve it.<br />
<br />
I wrote a brute force solver, but it was taking far too much time for puzzles of moderately-large size. One trick I used to speed it up was to do a first pass on each hint, seeing how many possible rectangles could go there at the beginning of the puzzle. Then we sort the hints from fewest to most possible rectangles, ensuring that we try all the "forced" ones first.<br />
<br />
I suspect I can make the solver even faster if I resort at each decision in the solver tree, because placing one rectangle frequently reduces the number of options for another, and I find that for most of the Shikaku I'm generating, every rectangle is forced at some point or another, leaving the best kind of brute-force tree ever: a straight line to the solution.<br />
<br />
There was another problem. Too often the generator would put two 2x1 rectangles next to each other, and too often the hint-picker would select kitty-corner hints from those rectangles, giving the puzzle multiple solutions.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCqyRYgswF4BWWyFifqSZ9qnv2xIz8JZNyhdmIpp68nSjbGwTEI7cjTyk8dbsgsNID5nz_79zV1ERVkjbGicVd_eB2xrV-cmt35jwo-J5_0TFDtxDYRIAq3VghhS8YtCrqV97hCpywYVI/s1600/shikaku-ambig.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCqyRYgswF4BWWyFifqSZ9qnv2xIz8JZNyhdmIpp68nSjbGwTEI7cjTyk8dbsgsNID5nz_79zV1ERVkjbGicVd_eB2xrV-cmt35jwo-J5_0TFDtxDYRIAq3VghhS8YtCrqV97hCpywYVI/s1600/shikaku-ambig.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">An ambiguous Shikaku.</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
When this happens, the solver takes longer to run, and in the end we have to throw all that work away because we won't accept a puzzle with multiple solutions. So I threw in a pass to detect when this was happening and auto-correct it (by moving one of the hints).<br />
<br />
That left me with a fast-enough generator and a not-so-fast-but-maybe-ok solver to verify the puzzles.<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL0LRIHfGoW1GzQe0tTNB2xcfxh6pRCuKyv8C57CdlvDRXmp4UFMpMxyjtJyF6zQjSyNomkXX2439H3AMSbZLjeRhOTqSJirkycrK2q6taXhzPsTBXMazhNeK5QggJ1-7X3ntNO1TrCH4/s1600/shikaku9.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjL0LRIHfGoW1GzQe0tTNB2xcfxh6pRCuKyv8C57CdlvDRXmp4UFMpMxyjtJyF6zQjSyNomkXX2439H3AMSbZLjeRhOTqSJirkycrK2q6taXhzPsTBXMazhNeK5QggJ1-7X3ntNO1TrCH4/s320/shikaku9.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A generated Shikoku-like puzzle.</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
For the other puzzle types, I have a feature that allows you to adjust the "difficulty" of the puzzle, but so far I don't have that for Shikaku. I could go by the number of choices made in the solver (which is what I did for Numbrix and Hidato), or I might even be able to use "number of initially-forced regions". However I would need to speed up the solver before I attempted that. At the highest grid-size setting, 15x15, it currently takes a while just to generate one puzzle, and if it started getting picky about difficulty it might take way too long.</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5MgdWg_CmqUjNcQElvruf3J12jXdwUza6CjSEa1pZajGlmnkpIgCKP5VAdXEUb2QF21Py1HH4uLq0Rm1QOYTO5MnNZ1v0zFpiswz2MwNWDTnsz1PTKpbBI5tN4HuZYz6HOQtaB4IEscQ/s1600/shikaku9b.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5MgdWg_CmqUjNcQElvruf3J12jXdwUza6CjSEa1pZajGlmnkpIgCKP5VAdXEUb2QF21Py1HH4uLq0Rm1QOYTO5MnNZ1v0zFpiswz2MwNWDTnsz1PTKpbBI5tN4HuZYz6HOQtaB4IEscQ/s320/shikaku9b.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Solving in progress.</td></tr>
</tbody></table>
The UI is pretty straightforward. You click and drag to make rectangles. It colors a rectangle red if it's invalid (i.e. if it contains no number, or more than one number, or doesn't have the right area for the number it contains).<br />
<div>
<div>
<br /></div>
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/probably-puzzles/demo/4puzzles/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Probably Puzzles</span></a></div>
</div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-72782934328561010332016-08-27T22:51:00.000-04:002016-08-27T22:51:04.141-04:00Generating NonogramsIn <a href="http://arbitraryclark.blogspot.com/2016/08/generating-numbrix-like-puzzles.html" target="_blank">my last post</a>, I discussed a generator for Numbrix-like puzzles. Since then I've extended <a href="https://sbj42.github.io/projects/probably-puzzles/demo/4puzzles/" target="_blank">Probably Puzzles</a> with three more puzzle types.<br />
<br />
<a href="https://sbj42.github.io/projects/probably-puzzles/demo/4puzzles/" target="_blank">Hidato</a> is very similar to Numbrix. The only difference is that the path can move diagonally. To get that I had to drop the Hamiltonian path generator I scrounged up, and replace it with one that generalizes to accept the diagonal connections. The new path generator is based on <a href="http://stackoverflow.com/a/1987274/72989" target="_blank">this stackoverflow answer</a>. Hidato is interesting, the path can cross itself and that seems to lead to some surprises while I'm solving it.<br />
<br />
<div style="text-align: right;">
</div>
Another puzzle type I added is <a href="https://en.wikipedia.org/wiki/Nonogram" target="_blank">Nonogram</a> (a.k.a. "Paint by Numbers", "Griddler", and lots of other names). This one is the most popular of the ones I've done so far (but will certainly drop to second place if I add Sudoku). In this puzzle you color in the cells of a grid to draw a "picture". There are numbers along the left and top of the grid which constrain which cells you can fill in. They tell you how many sequences there are on each row and column, and the lengths of those sequences, but not exactly where those sequences go.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHP-FRWYtsML7w84EDVFRO859m0Bpc3QmN5EBzTZM7bLgg2qvR-eEeOiNEotUFMP-hNrmsBzDcUSIntirYzOYVh8EruhIe1BDPJI4Ua7sYoSI50G-s2KislxjOk9pI-xDphoS4TsWOjpQ/s1600/nonogram9.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHP-FRWYtsML7w84EDVFRO859m0Bpc3QmN5EBzTZM7bLgg2qvR-eEeOiNEotUFMP-hNrmsBzDcUSIntirYzOYVh8EruhIe1BDPJI4Ua7sYoSI50G-s2KislxjOk9pI-xDphoS4TsWOjpQ/s320/nonogram9.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A randomly-generated 9x9 nonogram.</td></tr>
</tbody></table>
On the one hand, generating these is easier than Numbrix/Hidato. We don't need to pick a subset of the hints that leaves the puzzle uniquely solvable, we just need to generate a picture. The hints are determined directly from that. On the other hand, how do we generate "a picture"? I considered pulling from a library of images, or scattering various geometric shapes around, but I though it would be better for this particular site if the puzzles were completely random. In the end, I decided to use <a href="https://en.wikipedia.org/wiki/Simplex_noise" target="_blank">simplex noise</a>, which generates basically a bunch of random blobs. At low difficulty, you get very big blobs, and a high likelihood that some rows or columns are completely determined by the hints. At the highest difficulty, each cell is independently random, and it ensures that no rows or columns are completely determined.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqhg_pyj3vJzDCMFi6ACPDYoT-e6kGrgbQB-iXwCpkbooJ4pa3-yQnazXY4ywEnM-eJ61xMzN0YfsbCiXkn2jq_FxSqfaE0Eo-EcxVohmBJOr0E_hAElk8TvMjRXKSq8YjQ6cr50eoB3Q/s1600/nonogram9b.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqhg_pyj3vJzDCMFi6ACPDYoT-e6kGrgbQB-iXwCpkbooJ4pa3-yQnazXY4ywEnM-eJ61xMzN0YfsbCiXkn2jq_FxSqfaE0Eo-EcxVohmBJOr0E_hAElk8TvMjRXKSq8YjQ6cr50eoB3Q/s320/nonogram9b.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Nonogram in progress.</td></tr>
</tbody></table>
For the play interface, I took some advice from my wife, who is an avid nonogram puzzler. You left click to fill the cells, and right-click to place little dots in the cells which you can use as a reminder to yourself that you've decided that particular cell <i>must</i> be empty.<br />
<br />
The third puzzle type I added was <a href="http://www.nikoli.co.jp/en/puzzles/shikaku.html" target="_blank">Shikaku</a>, which I'll describe in the next post.<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/probably-puzzles/demo/4puzzles/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Probably Puzzles</span></a></div>
<br />James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-15259917851647383112016-08-21T13:16:00.000-04:002016-08-21T13:24:24.943-04:00Generating Numbrix-like Puzzles<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP3RMjYGwPYxwzXTBuQZaSF7YdJeGk8KckFv1w4aSm31MErmhnCgM5iH3lkaAtakW7tsHdIlH_aCuYtXu15fACGLRekGn37glUnf27FiRTAXIiDXVKbgleauRQWb5Q8smYInd5veBhyphenhypheneQ/s1600/numbrix.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjP3RMjYGwPYxwzXTBuQZaSF7YdJeGk8KckFv1w4aSm31MErmhnCgM5iH3lkaAtakW7tsHdIlH_aCuYtXu15fACGLRekGn37glUnf27FiRTAXIiDXVKbgleauRQWb5Q8smYInd5veBhyphenhypheneQ/s1600/numbrix.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Very tiny<br />
Numbrix-like<br />
example.</td></tr>
</tbody></table>
We got the kids interested in a simple kind of logic puzzle called Numbrix. I showed my son a multiplication table and he became obsessed for a time with filling out more and more tables with bigger and bigger numbers. Since he seemed to like writing numbers into boxes, we looked around for a puzzle we could print off the web that he could do, and found some kids' Numbrix at <a href="http://www.mathinenglish.com/numbrix.php" target="_blank">this "Math in English" site</a>.<br />
<br />
Numbrix is apparently a puzzle <a href="http://parade.com/tag/numbrix/" target="_blank">published</a> in Parade magazine, developed by Marilyn vos Savant, who had the Guinness Book of World Records "Highest IQ" in the late eighties and also a serendipitous surname. The goal is to complete a Hamiltonian path on a grid with consecutive numbers. If you've heard of Hidato, it's like that but you can't move diagonally. The ones at Math in English seem to be randomly generated, but the ones made by vos Savant are cleverly constructed by hand.<br />
<br />
Either way, there are a limited number of Numbrices at both places. I haven't found anything that generates them "while u wait", so I set about writing <a href="https://sbj42.github.io/projects/probably-puzzles/demo/numbrix/" target="_blank">some code to do that</a>.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8dFS_10V055-fakQPi5ohG-NBRZXtB162vDCvtb78w_wEo6drUmoW8uJNnRzSw1Xwi9YRt8r5QT6J5Bvf3BmErd-Mc02NsoU3LJw6H3UxTDJb0GuucYjphRUBL2mLotCIfAsLswU2cnQ/s1600/numbrix9.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8dFS_10V055-fakQPi5ohG-NBRZXtB162vDCvtb78w_wEo6drUmoW8uJNnRzSw1Xwi9YRt8r5QT6J5Bvf3BmErd-Mc02NsoU3LJw6H3UxTDJb0GuucYjphRUBL2mLotCIfAsLswU2cnQ/s320/numbrix9.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">A randomly generated Numbrix-like puzzle.</td></tr>
</tbody></table>
The first step is to generate a random-ish Hamiltonian path. With a little googling I came across <a href="https://github.com/oysterCrusher/hampath" target="_blank">this "HamPath" library</a>, which does the trick well enough. Next we need to take away random numbers until we have a puzzle we like. The puzzle has to have a unique solution, so each time we take away a number we have to check to see if there's another solution, so we need a solver. The solver just runs all the possibilities and stops early if it finds a second solution.<br />
<br />
I wanted it to have a difficulty knob, so we need some kind of measurement of difficulty and then we need to make sure when we take a number away we haven't made it too difficult. I use two measures of difficulty: "choice count" and "stretch".<br />
<br />
Choice count is a measure of how many branches were encountered in the solver. This vaguely represents the number of ways you can go wrong if you just jot down numbers without thinking or looking ahead. It's not perfect, because sometimes the path forward is obvious to a human but not to the solver. It measures a choice at each number, but it would be better if it considered sequences of numbers that link two given cells. For instance, starting with 1 in the above picture, you need to reach the 6. There are a few ways to get to the 6, and the solver counts those, but it also counts ridiculous choices that never reach the 6, and I don't think humans would count those as making the puzzle more difficult.<br />
<br />
The other measure is stretch. Stretch is just the maximum distance between two given numbers. A smaller stretch generally means you need to look ahead a smaller distance to find the next number, and it tends to mean fewer paths between one number and the next. This one also has the benefit that it speeds up the solver; a very large gap in the numbers causes the solver to consider a large number of possible paths and while that might make an interesting puzzle, we need the solver to finish in a reasonable amount of time.<br />
<br />
So we take away numbers, making sure the solution is unique and keeping the difficulty in check, and then stop after a certain number of tries, and now we have a puzzle. The rest is user interface.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUFUnrpLt0jk0Xbbma0FjHXVS-bD-hu_A-L0DtPPXMgQTy_Xo73_LthXdiOG-BEWtMwt9hBx7F94XeOkdvuqKAO3POJAMB9BbJkmA79Nq7WEoJr6oiGijP6A6j-jDKKn5T2D5X1Ajtu6A/s1600/numbrix9b.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUFUnrpLt0jk0Xbbma0FjHXVS-bD-hu_A-L0DtPPXMgQTy_Xo73_LthXdiOG-BEWtMwt9hBx7F94XeOkdvuqKAO3POJAMB9BbJkmA79Nq7WEoJr6oiGijP6A6j-jDKKn5T2D5X1Ajtu6A/s320/numbrix9b.png" width="274" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Puzzle in progress.</td></tr>
</tbody></table>
<br />
I tried to keep the interface simple. The Parade site requires you to type the numbers, which I found annoying. Instead I wanted to just click to place the next number. There are some edge cases to consider, however. The user should be able to start at any number, and go in both directions. Also, the user needs to be able to remove numbers they've entered. And when you hit a sequence you've already placed, it should skip over that to set you up to put the next number after the sequence.<br />
<br />
As you'll guess from the intro menu, I'm thinking of adding more puzzle types later.<br />
<br />
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/probably-puzzles/demo/numbrix/" target="_blank">Probably Puzzles</a></span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com1tag:blogger.com,1999:blog-929851985019559053.post-69422549904049035342016-08-15T23:32:00.001-04:002016-08-27T14:20:28.957-04:00It's EELectric! (Junior)<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuyPyUZ_1HPFwpKQvzehIOpIlp-fVPV4dejqjOvctWybAhrhpkvAlpDwDWSj9Lfr8NaZVFATshCl5ciHjMVgXk5WcLYIsk7buCStuQzN4X7-3NIZ2JjlDHh_DMmUL2H3pSBMZfjFSTdmg/s1600/title2.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" height="108" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuyPyUZ_1HPFwpKQvzehIOpIlp-fVPV4dejqjOvctWybAhrhpkvAlpDwDWSj9Lfr8NaZVFATshCl5ciHjMVgXk5WcLYIsk7buCStuQzN4X7-3NIZ2JjlDHh_DMmUL2H3pSBMZfjFSTdmg/s200/title2.png" width="200" /></a>My kids really wanted to play <a href="http://arbitraryclark.blogspot.com/2016/07/its-eelectric.html" target="_blank">It's EELectric!</a>, but it's way too hard for them. So I made a new version with simpler rules and easier levels. You know, for kids. Or adults who couldn't stand the punishment of the previous version.<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/its-eelectric-jr/" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">It's EElectric! (Junior)</span></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeWK6-pg_jQWupVF7CB5xzQ2bWCHe4lxOnbgL9UlBQhqbEMEjQljMQBkLigBcUtQ81h06R5YidmbGieCiX0wpTKKGdrGbjg280K1S7VxXkmfj0hDjC5fKjFpeMi7zofsr9IsrUNUiA_5I/s1600/eely.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeWK6-pg_jQWupVF7CB5xzQ2bWCHe4lxOnbgL9UlBQhqbEMEjQljMQBkLigBcUtQ81h06R5YidmbGieCiX0wpTKKGdrGbjg280K1S7VxXkmfj0hDjC5fKjFpeMi7zofsr9IsrUNUiA_5I/s200/eely.png" width="200" /></a></div>
<br />James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-61898436173982979852016-07-25T21:04:00.001-04:002016-07-25T21:04:42.308-04:00Music Theory, Sound Science and Digital Audio<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23SOhfrsHAN0UU8c5YTOohUD-CHcWG4jQ2yw4jJ2lYmYNxLuUOnj5HCoikHBzs-gqlzl5zt-m59WWGgQSyPiJwGrwlkM26GTGIBvcrUiK9DcZnWW45GKqJUJgJZSHVEi5mnwvAW2eXrM/s1600/octave.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj23SOhfrsHAN0UU8c5YTOohUD-CHcWG4jQ2yw4jJ2lYmYNxLuUOnj5HCoikHBzs-gqlzl5zt-m59WWGgQSyPiJwGrwlkM26GTGIBvcrUiK9DcZnWW45GKqJUJgJZSHVEi5mnwvAW2eXrM/s1600/octave.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">This isn't the demo.<br />
This is<span style="font-size: 12.8px;"> just a screenshot.</span></td></tr>
</tbody></table>
I've been working on some JavaScript music code for a possible future project. So far I've learned a few interesting things about music theory, sound, and digital audio, and I've reached a milestone so here's <a href="https://sbj42.github.io/projects/mu-music-library/demo/keyboard-demo/demo/keyboard.html" target="_blank">Keyboard Demo</a>, a little electronic keyboard demo.<br />
<br />
Here are some things I needed to learn to get this far:<br />
<br />
<h3 style="text-align: center;">
Generating audio with FFmpeg</h3>
<br />
I wanted to be able to play a range of pitches; the standard 88-key piano keyboard runs from C0 to C8, so at least those. I also wanted to support IE. There don't seem to be a lot of good baked-in options for that sort of thing, so I knew the lowest common denominator would be to have a bunch of tiny audio files, one for each pitch.<br />
<br />
I considered generating all the pitches with MIDI in <a href="http://www.anvilstudio.com/" target="_blank">Anvil Studio</a>, recording with <a href="http://www.audacityteam.org/" target="_blank">Audacity</a>, and then cutting that audio up into individual files, but that would have taken forever. After poking around, I discovered that you can generate audio in batch using <a href="https://ffmpeg.org/" target="_blank">FFmpeg</a>.<br />
<br />
FFmpeg is a very useful tool for audio and video manipulation. It can transcode audio and video files, change sample- and frame- rates, apply filters, crossfade, and probably hundreds of other tricks I don't even know about. I didn't know it could generate audio (or video) until I ran into <a href="https://superuser.com/questions/724391/how-to-generate-a-sine-wave-with-ffmpeg" target="_blank">this superuser.com question</a>. The idea is to use ffmpeg's filtergraph feature as an input, with a command line like this:<br />
<br />
<div style="text-align: center;">
<span style="color: white; font-family: "courier new" , "courier" , monospace;"><span style="background-color: black;">ffmpeg -f lavfi -i "<filtergraph string>" <output></span></span><br />
<span style="color: white; font-family: "courier new" , "courier" , monospace;"><span style="background-color: black;"><br /></span></span></div>
FFmpeg's filtergraph strings are pretty complicated, but making a sine wave is pretty easy:<br />
<br />
<div style="text-align: center;">
<span style="background-color: black; color: white;"><span style="font-family: "courier new" , "courier" , monospace;">sine=frequency=440:duration=1</span></span><br />
<span style="background-color: black; color: white;"><span style="font-family: "courier new" , "courier" , monospace;"><br /></span></span></div>
<div style="text-align: left;">
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit7x7Du314c3nZnaSW6zJHVIR-Aokp3IdVe8OfORVrR1wBXaQsZ8cqUQtdLKiu14gCqIVpc1R6rJAhAG6fdiKkjA2tqs3FBoKro_y0JDxspW_R5V2IVlikuEWT6KfyhBhSgGxyzYOOEnA/s1600/sineA4.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="113" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEit7x7Du314c3nZnaSW6zJHVIR-Aokp3IdVe8OfORVrR1wBXaQsZ8cqUQtdLKiu14gCqIVpc1R6rJAhAG6fdiKkjA2tqs3FBoKro_y0JDxspW_R5V2IVlikuEWT6KfyhBhSgGxyzYOOEnA/s320/sineA4.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Yup. That's a sine wave.</td></tr>
</tbody></table>
<br />
A quick python script to generate 88 calls to FFmpeg and now I've got all the pitches I need as individual <a href="https://en.wikipedia.org/wiki/MPEG-4_Part_14" target="_blank">m4a</a> files (IE won't play ogg, sadly). There's some interesting music theory in just the choice of frequencies, of which I'll give a taste here:</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
I generated pitches using <a href="https://en.wikipedia.org/wiki/Equal_temperament" target="_blank">equal temperatment</a>, meaning that each pair of adjacent pitches has the same frequency ratio. As temperaments go, this is the one that all the cool kids are using. It beats out well and meantone temperament, and generally makes sense if you want to play in multiple keys using the same set of pitches. Apparently it produces some slightly impure intervals, though I haven't yet compared it to just intionation with my own ear.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Then I based the frequencies around the notion that A4 (the A above middle C) is 440 hertz. That's called <a href="https://en.wikipedia.org/wiki/Concert_pitch" target="_blank">concert pitch</a>, or at least that's what we call it lately, and it's not the only pitch reference out there. It's nice that it's a round number like that - makes it easier to remember. I've heard it said that music played in different keys can "feel" different, as though simply transposing a piece upward a semitone can turn it from melancholy to hopeful, or something. But the fact that the exact frequency of a pitch has changes through the ages, and that different ensembles might choose something other than the standard, makes me a little skeptical. Some people have perfect pitch, of course, but I don't know how that works with respect to different pitch references. I guess both the feeling-of-the-key and perfect-pitch concepts are relative to contemporary practice. Anyway, 440.<br />
<br /></div>
<h3 style="text-align: center;">
My ears</h3>
<div style="text-align: left;">
<br />
The range of frequencies that humans can hear is (very roughly) 20 Hz to 20 kHz, which are nice round numbers that should make you suspicious about the error bars around them. I don't know what the standard deviation is, but I was surprised when I went to listen to those audio files I made. I couldn't hear most of the low octave (C1-A1, ~32-55Hz)! I was sure that I had made a mistake in my script. I've played an 88-key piano before and while my memory, like my pitch, isn't perfect, I seem to recall being able to hear those low notes.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
I ran a highly scientific test using <a href="https://www.youtube.com/watch?v=qNf9nzvnd1k" target="_blank">this YouTube video</a>, and sure enough, my ears don't kick in until around 55 Hz. I didn't get to check on the high end of my range because the dogs started barking and I can't blame them because those are some annoying frequencies up there.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
So what's going on? Well, as you know, a piano doesn't sound like a sine wave. It produces <a href="https://www.youtube.com/watch?v=5xjD6SRY8Pg" target="_blank">a crazy mess of noise</a> that goes well beyond the fundamental frequency. Some of the biggest frequencies in the spectrum are multiples of the fundamental frequency (called <a href="https://en.wikipedia.org/wiki/Harmonic" target="_blank">"harmonics"</a>), but also the whole audible range is spattered with low-amplitude impurities, and all that stuff together makes the timbre of the note. That's why those frequency spectrum visualizers are never very satisfying to me - it's all a mess, all the time. I guess it turns out that when I play C1 I'm not usually hearing the fundamental frequency at all (although more volume can help), but my brain is putting the harmonics and other noice together and I end up with a good approximation.<br />
<br /></div>
<h3 style="text-align: center;">
Adding harmonics</h3>
<div style="text-align: left;">
<br />
So I went back to those sound files and added some harmonics. <a href="http://stackoverflow.com/a/27513309/72989" target="_blank">This stackoverflow answer</a> has some numbers in it for relative frequencies of harmonics for a piano, supposedly. I don't know where they got those numbers from, but I figured it was better than just guessing. I plugged those into my script and generated some more complicated FFmpeg filtergraphs. For example, heres A4 with just the first three harmonics:<br />
<br /></div>
<div style="background-color: black; color: white; font-family: "courier new" , "courier" , monospace; text-align: center; word-wrap: break-word;">
sine=frequency=440.00000:duration=1[s1];<br />
[s1]volume=volume=3[i1];<br />
sine=frequency=880.00000:duration=1[s2];<br />
[s2]volume=volume=1.197[i2];<br />
sine=frequency=1320.00000:duration=1[s3];<br />
[s3]volume=volume=0.897[i3];<br />
[i1][i2][i3]amix=inputs=3</div>
<div style="text-align: left;">
<br />
The actual filtergraph I used was a little more complex: more harmonics, plus I added a delay to each harmonic so that I didn't end up with a sawtooth-like shape.<br />
<br />
Now I can hear the low notes. Plus, the notes are a little less annoying. It's interesting that adding frequencies other than the one I want to play - impure, dirty, frequencies - ones that don't <i>belong</i> - actually make the sound more real.<br />
<br />
<h3 style="text-align: center;">
Aliasing</h3>
<div>
<br />
Hey wait a minute. I can hear the low octave now, just fine, but the high octave sounds just <i>terrible</i>. The notes I hear don't even seem related to the ones I'm asking for. Luckily, I remembered something from college about the <a href="https://en.wikipedia.org/wiki/Nyquist_frequency" target="_blank">Nyquist frequency</a> (so I guess it wasn't a total waste). I had picked a sample rate of 16 kHz, because I wanted to save bandwidth and it made the audio files nice and small. Sure enough, if your sample rate is less than twice some frequency you'd like to hear then sorry, that's just not gonna happen. Instead you're going to hear a bizzaro mirror-world frequency, and you probably won't like it.<br />
<br /></div>
<div>
To me, the best analogy is to that old experiment where you look at a fan in a strobe light: at a certain strobe frequency the fan appears to stop spinning, and then if you strobe faster the fan seems to rotate backwards. It's not a perfect analogy, but basically this is the kind of crazy stuff that happens when analog meets digital. I probably could have fixed it by filtering out the high frequencies or increasing my sample rate, but these audio files are just for IE users. I can just drop those pitches from the range. The IE users probably won't miss them. What are the non-IE users going to get?<br />
<br /></div>
<div>
<h3 style="text-align: center;">
The Web Audio API</h3>
</div>
<div>
<br />
Modern browsers have the <a href="https://www.w3.org/TR/webaudio/" target="_blank">Web Audio API</a> built-in, and with that I don't need any silly audio files laying about. I can generate my noises in real time! It's as simple as this:<br />
<br /></div>
<div style="background-color: black; color: white; font-family: "courier new" , "courier" , monospace; text-align: center; word-wrap: break-word;">
<div style="text-align: left;">
var context = new AudioContext();</div>
<div style="text-align: left;">
var oscillator = context.createOscillator();</div>
<div style="text-align: left;">
oscillator.frequency.value = 440;</div>
<div style="text-align: left;">
oscillator.connect(context.destination);</div>
<div style="text-align: left;">
oscillator.start();</div>
</div>
<div>
<br />
That's produces a pure A4 sine wave. Add more oscillators for the harmonics, watch out for aliasing, put a "gain" node in there because the default oscillator is super loud, and we're in business. There are some cool tricks like amplitude envelopes that I could use to make better or crazier sounds (see <a href="http://websynths.com/" target="_blank">this web synthesizer</a> site for instance). Really, the engineering of synthesizers is an enormously deep rabbit-hole, and I'm tempted to jump in, but this will do for now.<br />
<br />
<h3 style="text-align: center;">
Volume</h3>
<br />
Another thing about sounds: Loudness. I used to think it was mostly about how far up and down the waveform moved. Nope. It's also not about the rate-of-change of the waveform at any given point. If you're thinking about amplitude of sine waves, that's closer, but still not right.<br />
<br />
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOjXZUo7hqTf_y-VilukzBe8vvW95EINF14BxIOvmq8hnibZDrIR4mQRuRr6fghAMeUSYDNMwh5uA0zJLrnD39nzMCsYYiG3gO6aXCjdSH_8NqfaUffBLO5JW5bLlR_jwC7wJEHOCXmWA/s1600/loudness2.png" imageanchor="1" style="clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOjXZUo7hqTf_y-VilukzBe8vvW95EINF14BxIOvmq8hnibZDrIR4mQRuRr6fghAMeUSYDNMwh5uA0zJLrnD39nzMCsYYiG3gO6aXCjdSH_8NqfaUffBLO5JW5bLlR_jwC7wJEHOCXmWA/s1600/loudness2.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Uh-uh.</td></tr>
</tbody></table>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; margin-right: 1em; text-align: left;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpl8TNnYK18yRtByIEHvrvzdzorMxZ396nQuOXttNAOFDzJuJkshrL6J2cwq_GQdEu24wnPUlMHJnlOQ8uYulzGR6AJe_6fnXcWd1Vj-9oQKEAhrwT3TqhGBR_EFPcBMMer6E3-karBgI/s1600/loudness.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpl8TNnYK18yRtByIEHvrvzdzorMxZ396nQuOXttNAOFDzJuJkshrL6J2cwq_GQdEu24wnPUlMHJnlOQ8uYulzGR6AJe_6fnXcWd1Vj-9oQKEAhrwT3TqhGBR_EFPcBMMer6E3-karBgI/s1600/loudness.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Nope.</td></tr>
</tbody></table>
<div style="clear: both;">
</div>
From what I now understand, it's a combination of the amount of energy in the wave over a certain span of time, and the sensitivity of the human ear to the frequencies involved. A big spike in the wave means the speaker (for example) has to push a bunch of air molecules really hard, but it has to keep working on that air for a while before it will affect how loud we think the sound is. Plus, even a very energetic sound may be very quiet if it's near the edges of the range of our hearing, like those low-octave pure-sine-wave notes I couldn't hear. Loudness is a messy concept, and, when it comes to commercial music, thank goodness for <a href="https://en.wikipedia.org/wiki/ReplayGain" target="_blank">ReplayGain</a>.<br />
<br />
In my demo you'll notice that some of the notes are louder than others, due partly to the fact that I didn't vary the oscillator amplitudes as the pitch went up, but also due to a phenomenon represented well by <a href="https://en.wikipedia.org/wiki/Fletcher%E2%80%93Munson_curves" target="_blank">the Fletcher-Munson curves</a>, which show that some frequencies just sound louder than others even when the "sound pressure level" is the same.<br />
<br />
<h3 style="text-align: center;">
The interface</h3>
</div>
<div>
<br /></div>
<div>
The interface for this demo is pretty simple: Click and hold a key to play it. If you want to get fancy, you can hold down shift and click different notes to make chords. I took a tape ruler to the little electric keyboard I have for some measurements and then used a little dynamic SVG to generate the keys. There's a little dot on middle C just to get you oriented. That's about it. I have some bigger plans for this code, so stay... <i>tuned</i>. See what I did there? Tuned? It's.. oh, ok. Here's the link again:</div>
<div>
<br /></div>
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/mu-music-library/demo/keyboard-demo/demo/keyboard.html" target="_blank"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;">Keyboard Demo</span></a></div>
</div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-55520396991647231902016-07-18T23:25:00.000-04:002016-07-18T23:25:14.233-04:00It's EELectric!Do you like eel-based puzzle games? Ready for some mind-bending diagonal-moving action? No? Nevertheless, let me introduce you to <a href="https://sbj42.github.io/projects/its-eelectric/" target="_blank">It's EELectric!</a>, an original puzzle game I've been working on.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbxk4JginqNX16OD_McuD2JOO2GvuQv-NNxNtFP8sVef4oWuRTmhXTNdXPeHbJAl_mSxIEkIkXo7xTkKvod5q2zGtpyeQvNjxbZU9nAa1NS9erL0qn_Rnbg81YXzRqO4K8NWEFLyMw7Y4/s1600/title2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="140" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbxk4JginqNX16OD_McuD2JOO2GvuQv-NNxNtFP8sVef4oWuRTmhXTNdXPeHbJAl_mSxIEkIkXo7xTkKvod5q2zGtpyeQvNjxbZU9nAa1NS9erL0qn_Rnbg81YXzRqO4K8NWEFLyMw7Y4/s320/title2.png" width="320" /></a></div>
In this game you star as an apex predator, the deadly <i><a href="https://en.wikipedia.org/wiki/Electric_eel" target="_blank">Electrophorus electricus</a></i>, better known as the electric eel. This is not a realistic eel simulation. In fact, I looked it up: electric eels don't look like this thing at all.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBz19RPRPMWBttDXMnHRPDMbyPQdhlP7JeQu3YUeXdi8kkrm40qBmdYG2XPTnhXc6X1BiNn8i07EGWkma4UOQyggybnydtAOwPhM8I8mTB6oBUByrnjzN12UsDKGiiNsKrF1gUbj5cG2k/s1600/eely.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBz19RPRPMWBttDXMnHRPDMbyPQdhlP7JeQu3YUeXdi8kkrm40qBmdYG2XPTnhXc6X1BiNn8i07EGWkma4UOQyggybnydtAOwPhM8I8mTB6oBUByrnjzN12UsDKGiiNsKrF1gUbj5cG2k/s1600/eely.png" /></a>Plus, I don't think there starfish in the same... you know what, just swim with it. Your job is to eat all the fish on the level, but before you chow down on the unsuspecting prey you have to kill it with your electric shock attack. And you're hungry and getting hungrier, so you'll have to be careful not to waste any moves. Otherwise you'll perish and have to press R to go back in time to try again. I already mentioned it's not realistic. SHOCKING.<br />
<br />
Enjoy 37 fiendfishly difficult pools of funderwater puzzling! Sound sea-ffects? It's um, reel.. something. Hmm.<br />
<br />
Anyway here it is:<br />
<br />
<div style="text-align: center;">
<a href="https://sbj42.github.io/projects/its-eelectric/" target="_blank"><span style="font-family: Courier New, Courier, monospace; font-size: large;">It's EELectric!</span></a></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-33269526370168958782016-05-25T20:48:00.001-04:002016-05-25T20:48:52.516-04:00Callooh Callay, World!I wrote some puzzles for the <a href="http://www.mit.edu/~puzzle/2014/" target="_blank">2014 MIT Mystery Hunt</a>. For one<span style="font-family: inherit;"> o<span style="font-family: inherit;">f them, </span></span><span style="font-family: inherit;"><a href="https://sbj42.github.io/projects/callooh-callay-world/" style="font-family: 'Courier New', Courier, monospace; font-weight: bold;" target="_blank"><span style="font-family: inherit;">Callooh Callay, World!</span></a><span style="font-family: inherit;"><span style="font-family: inherit;"><span style="font-family: inherit;">, I </span>inv</span>ented a new <a href="https://esolangs.org/wiki/Esoteric_programming_language" target="_blank">esoteric programming language</a> called Wonderlang.</span></span><br />
<span style="font-family: inherit;"><span style="font-family: inherit;"><br /></span></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheN34Kqb9-3uxWLfFZ2q12ERzEmBjX8yVq_THUvIYtJAsVWCmEN_PAfdQrRIs5xuuqRu0aIrLa1YMDhCwCrGsDlRZORdSumnH1hpajEzCbGaRg6XCT8_B2zw5BcW9ZnSS02PPfIh22XPY/s1600/chortle.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheN34Kqb9-3uxWLfFZ2q12ERzEmBjX8yVq_THUvIYtJAsVWCmEN_PAfdQrRIs5xuuqRu0aIrLa1YMDhCwCrGsDlRZORdSumnH1hpajEzCbGaRg6XCT8_B2zw5BcW9ZnSS02PPfIh22XPY/s400/chortle.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">It's frabjous.</td></tr>
</tbody></table>
<span style="font-family: inherit;"><span style="font-family: inherit;"><br /></span></span>I imagine this is how one would program computers in Wonderland, if there were any. It's not meant to be particularly difficult to use, but it has a different perspective. It requires a few characters not found in the ASCII character set: I suspect keyboards in Wonderland would have a great deal more keys than ours.<br />
<br />
Wonderlang hasn't made it onto <a href="https://esolangs.org/wiki/Main_Page">esolangs.org</a> (yet). To be fair, I haven't written a proper specification for it, which seems likely to be a requirement. But then that's probably another natural characteristic of programming in Wonderland - why should they need to write this stuff down, or if they did, despite needing to, why should they go and actually do it? After all, they're all mad there.<br />
<span style="font-family: inherit;"><br /></span>
<br />
<div style="text-align: center;">
<span style="font-family: inherit;"><span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/callooh-callay-world/" target="_blank">Callooh Callay, World!</a></span></span><br />
<span style="font-family: inherit;">[<a href="https://sbj42.github.io/projects/callooh-callay-world/solution.html" target="_blank">solution</a>]</span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com0tag:blogger.com,1999:blog-929851985019559053.post-43765713266834594132016-05-11T23:36:00.000-04:002016-05-12T22:12:58.784-04:00Hexfold<div style="text-align: left;">
<span style="font-family: inherit;">Here is <a href="https://sbj42.github.io/projects/hexfold/" target="_blank">HEXFOLD</a>, a web-based puzzle game inspired by the puzzle toy <a href="http://www.sciencewiz.com/Games/science_thinking_Games_Cool_Circuits.php" target="_blank">Cool Circuits</a>. For some background, see <a href="http://arbitraryclark.blogspot.com/2016/04/cool-circuits.html">my post analyzing Cool Circuits</a>, and <a href="http://arbitraryclark.blogspot.com/2016/05/cool-circuits-hexagons.html">the one describing how to extend Cool Circuits to a hexagonal board</a>.</span></div>
<div style="text-align: left;">
<span style="font-family: inherit;"><br /></span></div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: right; margin-left: 1em; text-align: right;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB70aroS62YmI9tMD6WBpG3PZ3y6ciwyFkS1hsCUYlYCeUND5kgjfPCSSswsH03oQWLi5NEfNKSVpYLxi9VvB3Okswrrgpn2ddoMxKdr9REeDey_6je0rqoSeuvaC1YbUXUy0RdH08FK8/s1600/l07.png" imageanchor="1" style="clear: left; margin-bottom: 1em; margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB70aroS62YmI9tMD6WBpG3PZ3y6ciwyFkS1hsCUYlYCeUND5kgjfPCSSswsH03oQWLi5NEfNKSVpYLxi9VvB3Okswrrgpn2ddoMxKdr9REeDey_6je0rqoSeuvaC1YbUXUy0RdH08FK8/s320/l07.png" width="291" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Level 7: "Lucid Sitar"</td></tr>
</tbody></table>
<div style="text-align: left;">
<span style="font-family: inherit;">TL;DR: The object of the game is to place the pieces on the board to create a loop, using every piece and obeying certain constraints that are different for each level. I made 36 levels (the 37th and final one is randomly generated) which more or less increase in difficulty from hand-holding tutorial to absurdly hard.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">I should warn you, this isn't an easy, kid game like <a href="http://arbitraryclark.blogspot.com/2016/03/hello-robot-quest.html">Robot Quest</a> or a simple, casual game like <a href="http://arbitraryclark.blogspot.com/2016/04/logoplex.html">Logoplex</a>. Oh, sure, it starts out easy enough. But that doesn't last.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">If you give it a try, please provide feedback in the comments below.</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: inherit;">Before you ask: the level names are just arbitrary adjective-noun combinations. I generated a bunch using <a href="http://arbitraryclark.blogspot.com/2016/03/the-sillifier.html">The Sillifier</a>, and picked out the ones that amused me.</span></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<span style="font-family: "courier new" , "courier" , monospace; font-size: large;"><a href="https://sbj42.github.io/projects/hexfold/" target="_blank">HEXFOLD (Web)</a></span><br />
<br />
<div style="text-align: left;">
It's also available as a Chome App:</div>
<div style="text-align: left;">
<br /></div>
<span style="font-family: Courier New, Courier, monospace; font-size: large;"><a href="https://chrome.google.com/webstore/detail/hexfold/abmdjkdophekkfdeinpiklbabklhemch" target="_blank">HEXFOLD (Chrome Store)</a></span></div>
James Clarkhttp://www.blogger.com/profile/08057872204445328560noreply@blogger.com1