Tuesday, March 8, 2016

Conway's Game of Life in the Nand2Tetris Hack Machine

Nand2Tetris is a very interesting course that steps you all the way from simple logic gates up to an operating system. One of my sons is taking the course this semester, so I decided to shadow the assignments and ended up having a lot of fun.

Here's a quick walkthrough of an implementation of Conway's Life for the Hack machine.

You can find the source code here. Enjoy!

Thursday, April 30, 2015

Mac OS / Linux : Finding all the files that meet some criteria and returning an escaped list of the paths, sorted by length

This is another post "for the next poor bastard"

One of the side effects of running AnimEigo is that I end up with a lot of video on hard disc -- terabytes and terabytes of it. This is amusing when you consider my first hard drive cost $5000, stored 20MB, and shook the table when the disc heads moved.

When projects are finished, we archive all the video and project files just in case we need them later, and rather than just copying the files onto an archive disc (easy and fast), since much of the original source materials are uncompressed audio and video, compressing these files before archiving means we can save a few bucks and free up some hard drives for reuse and replicate the files on multiple drives as insurance.

The Mac finder has a built-in feature that lets you compress files and folders, but it occurred to me recently to check to see if there were better options.

After doing a little research, I settled on using pbzip2, the multicore implementation of bzip2, which seems to do a good job of compressing uncompressed video files -- often down to 20-25% of the original size. If you're using a Mac, the easiest way to install it is by using the fink package manager.

As pbzip2 is a command-line tool, you invoke it using the Terminal app, by typing something like this:

pbzip2 -v "path to the first file you want to compress" "path to the next file" ...

and pbzip happily goes off and (slowly) compresses the file for you. All fine and good, and you can just type the pbzip2 -v part and then drag files in from a folder window to enter the paths.

However, because I'm lazy and thus willing to spend many hours automating things to save myself a few seconds of drudgery, I started playing around in the default Bash shell that Terminal provides; it had been a while since I'd done more than trivial things in it and a refresher couldn't hurt.

The basic philosophy of Unix shell tools is "lots of little tools that do a small number of things well that you can hook up to do something complicated". You send the output of one tool into the next tool using a pipe, represented by the | character. Here's the command sequence I came up with:

find . \( -iname '*.aiff' -or -iname '*.aif' -or -iname '*.wav' -or \( -size +500000 -iname '*.mov' -not -iname '*ProRes*' -not -iname '*H264*' \) \) -print0 2> /dev/null | xargs -0 du -s | sort -n | cut -f 2 | while read line; do printf "%q " "$line" ; done ; echo

Here's what it does. The first part invokes the find command; this finds any file that ends in .aiff, .aif, .wav or .mov, with the added restriction that .mov files need to be at least 5GB long and have a name that doesn't include the strings ProRes or H264; this eliminates most if not all of the compressed video files. The -print0 command says to separate the output file paths with a nul character instead of a linefeed (needed so the next tool doesn't get confused by spaces in filenames), and the 2> /dev/null redirects any error messages to the great bit bucket in the sky.

Each path gets processed by xargs, which is a tool that lets you run other tools on each line. The -0 means use nul as the line delimiter, and it runs du -s (disk usage) on the file paths.

The output of that is a set of lines, each containing the length of the file in disk blocks plus the path, separated by a tab. This gets piped into the sort tool, which is told to sort the lines by their numeric value by using the -n flag; I want them in this order so pbzip2 can compress the smallest files first, freeing up space for the larger ones; often an archive drive will be almost full when I start to compress it.

Next the cut tool is used to extract the second field, which gets us back our list of paths, now sorted smallest to largest.

Finally, I need to put all these paths on a single line, separated by spaces, and properly escaped (spaces changed to "\ ", for example). There is a printf (print formatted) tool for this, but the "%q" formatting code that does the escaping is not implemented in the MacOS version of printf (bitch moan bitch moan). However, printf is also implemented as a built-in command in the Bash shell, and that version does implement "%q", so a little inline shell script will do what I need - it reads each line, prints it out escaped with a space after it, and then echos a blank line. The final result is a single long line containing all the file paths, which admittedly looks like crap but I can just copy it, type in pbzip2 -v (or any other compression command) and paste it in. Actually, given how pbzip2 spawns multiple threads and can chew up a lot of your cpu resources, you probably want to do something like nice -5 pbzip2 -v to make it a bit more polite.

This won't work if the filename has really weird characters in it, like carriage returns, but that isn't a problem for me.

Let me end with a big shoutout to all the contributors to the many postings on stackoverflow that helped me find the right tools and combinations.

PS: I later stumbled upon this excellent comparison of various compression tools which includes an efficiency/time tradeoff chart. Of course, depending on what you are compressing, your mileage may vary!

Friday, February 20, 2015

The anagram of Samarang is Anagrams

A few days ago I randomly stumbled upon the "check if two strings are anagrams" interview question, and it piqued my interest.

While it is immediately obvious there is an O(n-logn) algorithm for this -- sort the character arrays and compare them -- after a few moments I flashed on the O(n) algorithm for comparing two strings and determining if they are anagrams of each other.

This got me thinking, and I remembered the fun I had about 15 years ago assembling a huge list of english words and checking to see if the .com domain names were available. A little quick googling found some interesting word lists and I was off to the races.

I ended up implementing the O(n) comparison algorithm along with a data structure that chops up the word list by word length and then hashes the words using a hashing algorithm that causes potential anagrams to hash to the same bucket. However, checking all the words in each hash bucket is O(n^2)

Thus, it is typically much slower than the simpler nlogn algorithm in Python, because the lengths of the strings and the typical hash bucket are small, the python list.sort() function is highly optimized, and once you do have the strings and bucket sorted, the search for anagrams is O(n).

And O(n^2-logn) < O(n^2-n)

The moral of this story is, in order to figure out the best algorithm, you need to consider the whole problem. I fell in love with the O(n) comparison, but didn't notice for a while that changing the problem from "check if two strings are anagrams" to "find all the anagrams in a word list" made a big difference.

Still, it was a fun evening of coding, and my python is getting marginally less horrible.

You can find the python code, word lists and sample output here.

PS: Samarang is a place in Indonesia

Wednesday, January 7, 2015

Troubleshooting a Brother DCPL2540DW Wifi Configuration

This is one of those posts I'm mostly doing for the benefit of "The Next Poor Bastard". My Mom got a new DCPL2540DW printer the other day and it took a little headbanging to get it talking to the other devices in the house.

It's a fairly nice printer: built in scanner/copier, two-sided printing, WiFi with autoconfiguration, etc. Initial setup was easy, including one-touch wifi configuration.

Just one problem: you could see the printer on the network, but attempts to print to it just died -- nothing could talk to it. Connecting a machine to it via USB worked fine, so there was a problem with the configuration.

Condensing down about two hours of troubleshooting:

  • The Wifi status printout page doesn't tell you anything useful, but the Network menus have a status option that does -- stuff like IP address.
  • The Printer was connecting to the WiFi router fine, but instead of getting an address in the 192.168.2.X range like all the other devices, it was showing a 169.X.X.X address -- a self-assigned IP address.
  • My guess is that the WiFi router doesn't like to route packets to 169.X.X.X addresses, but probably accepts packets from them. So all the local devices could see the printer's "hey guys, open for business" announcement, but not do anything with it.
  • OK, several possible ways to fix this: tweak the router so it routes between 192.168.X.X and 169.X.X.X, force the printer to get its IP from the router, or the brute-force solution: give the printer a fixed IP address in the 192.168.2.X range.
  • So brute-force it is: go into the router configuration DHCP settings, and adjust the range of IP addresses it gives out to connected devices. It was, I changed it to .2-249. Then go into the printer's settings, and there's an option for setting the IP address -- set it to

Bing! Printing now works from everywhere; Macs, iPads (Airprint), even the phones.

Some other minor tidbits:

  • The Mac drivers package only installs on Mac OS 10.7 and higher, but Mom had an older 10.6.8 machine. However, you can download individual components (CUPS printer driver and Twain scanner driver) and they do install.
  • With the scanner driver installed, you can use Image Capture to control the printer and scan, even from a 10.6.8 machine.
  • You can print from a 10.5.X machine using the generic PCL 6 CUPS driver, but not over the network; you have to use the USB connection.

I'm probably going to try and get remote email printing working at some point, if so I'll update this post.

Brother Support Pages for Printer

Sunday, December 28, 2014

The Compex Cubes Puzzle

Often during the holidays, I do a little programming project based on something that just strikes my fancy. For example, one year I merged a bunch of dictionaries and then ran the words through DNS to see if they were available as domain names.

This year, my Mother -- Provider of Perfect Presents -- gave one of my sons an interesting puzzle called the "Complex Cube". It has 54 T-shaped wooden parts, each made of 4 unit cubes, and the challenge is to assemble them into a 6x6x6 cube.

I played with it a little bit, and soon got to the point where I realized it might be faster to write a program to analyse the problem, so given that I am a lazy person, I wrote a python script to solve it. It's really pretty simple, you just start at the bottom and scan across the rows and columns in each row, placing blocks in each empty position.

There are some easy optimizations you can do, like realizing there are only 12 possible block rotations you need to try because of assumptions you can make about positions you have already filled in, but the script still took anywhere from 5 minutes to 5 hours to generate a solution, even after doing some cute hacks to make it faster (like not using recursion, but keeping track of things with a state stack).

Then I realized that if I limit the search to solutions that are reflectively symmetric, the search space becomes much smaller, because I'm only solving a puzzle with 27 blocks instead of 54. Solution time goes down to under a minute and the solution looks nicer.

However, the output left a little to be desired...

Level 1
| P I N N N Q |
| L I M N H O |
| E I J K H H |
| E E G F H D |
| E A F F F D |
| A A A B C D |
Level 2
| P P M Z Q Q |
| L I M K X O |
| L J J K X O |
| V W G K X Y |
| T U G B C D |
| R R R B C S |

...and so on. So I got the idea of playing around with the Processing language to see if I could visualize it better. The result was a little script that displays the cube in 3D, lets you rotate and zoom it, and animates the solutions.

Needless to say, it took me twice as long to write the Processing visualization script as it did to write the Python solvers, and it probably took me twice as long to write the solvers as it would have taken to just solve the puzzle by hand.

But then, as I said above, I'm lazy... :)

You can find the scripts here if you'd like to play with them. Processing is built on top of Java and quite fun to play with.

Sunday, September 7, 2014

A Google Apps Sheet for STV Elections

I've been using Google apps like Sheets in my Kickstarter project, but one thing Sheets doesn't do is complex surveys. While you can link a survey to a sheet, the survey question types are pretty basic, and in particular it does not let you do a ranked survey (in other words, "here are N options, pick any number of them that you like and place them in order of preference.")

When I was on the EVE Online Council of Stellar Management, I became quite the election-systems nerd, because I was pushing hard for a reform of the election system. So just for fun, I decided to see if I could implement a Multiple-Choice Ballot in Google Apps.

What I ended up was a sheet with a bunch of embedded scripts that lets you:

  • Create a Multiple-Choice Ballot with optional additional questions (radios, dropdowns and fields) and voter validation.
  • Store the results in a sheet.
  • Export the results in the standard .blt ballot format used by many election systems.
  • And best of all, run the election using the Simple-STV and Wright-STV systems.
You can find the template sheet at http://stv.madoverlord.com/. Just make a copy of it in your own Google Drive and start playing with it.


Wednesday, February 5, 2014

Clever Combining CSV Files to Cure KickStarter Conundrums

I recently ran a successful KickStarter for a Bubblegum Crisis Ultimate Edition (you can still sign up here), and in the aftermath ran into an annoying problem.

KickStarter lets you run a Backer Survey after the project is funded, and that's how backers tell you things like their names, addresses and so on. A typical KickStarter will have many different "support levels" providing different benefits.

Annoyingly, KickStarter forces you to design a survey format for each individual support level, with no way of cloning surveys. But what really got me steamed was the discovery that when you export the survey results, you get a CSV file for each survey, and the format of that CSV file depends on the items in each individual survey.

That makes it a royal pain in the ass to import the data into your own database -- I mean, I had 38 CSV files, each with its own ordering of fields. Plus given that people take their time filling out the surveys (2 months later and they're still trickling in), I was going to be doing the import multiple times.

To solve this, I wrote a quick little python script, combine-csv.py, that reads in an entire directory of CSV files, extracts all the unique field names from them, and then outputs a single file containing all the rows of data, with a column for each unique field. In cases where a row does not have an entry for a particular field, it gets a blank entry. Also, if a file contains multiple columns that have the same name, the data in them is combined into a single column, separated by the | delimiter.

Added: optional count field name; if present, each unique line is emitted only once, with the last field being a count of the number of occurrences. If not present, the source file name is appended.

You can find it here: combine-csv.py.zip

You use it like this:

python combine-csv.py [source directory] [destination csv file] {optional count field name}


Update: I also whipped up a standalone AllCSV app that does the same thing; available for both Mac and Windows. It does not currently do the occurrence counting.