Tuesday, June 21, 2011

SQL: 3x3 Square Picture-Puzzle Solution

Update: A second attempt at a write up.

After Reilly solved a 3x3 square picture puzzle using Prolog I made a flippant remark that it would be interesting to solve using SQL and well, here goes...

First, create a table and populate it with the pieces:

# table of all pieces in all possible rotations
CREATE TABLE rotated_pieces (
  piece_id     INTEGER, # unique piece id
  rotation     INTEGER, # 0 - top, 1 - left, 2 - bottom, 3 - right
  top         CHAR(10), # animal at top edge
  top_head    CHAR(10), # head or tail at top edge
  `right`     CHAR(10),
  right_head  CHAR(10),
  bottom      CHAR(10),
  bottom_head CHAR(10),
  `left`      CHAR(10),
  left_head   CHAR(10));

# unrotated pieces (top)
INSERT INTO rotated_pieces 
       (piece_id, rotation, 
              top,       top_head, `right`,   right_head, bottom,    bottom_head, `left`,    left_head) 
VALUES (1, 0, 'cheetah', 'tail',   'tiger',   'tail',     'lion',    'head',      'tiger',   'head'),
       (2, 0, 'lion',    'tail',   'lion',    'head',     'tiger',   'tail',      'cheetah', 'head'),
       (3, 0, 'tiger',   'tail',   'lion',    'head',     'panther', 'head',      'tiger',   'tail'),
       (4, 0, 'panther', 'tail',   'cheetah', 'head',     'panther', 'tail',      'lion',    'tail'),
       (5, 0, 'tiger',   'head',   'tiger',   'head',     'cheetah', 'head',      'lion',    'tail'),
       (6, 0, 'panther', 'tail',   'panther', 'head',     'cheetah', 'tail',      'tiger',   'head'),
       (7, 0, 'panther', 'head',   'cheetah', 'tail',     'cheetah', 'head',      'lion',    'tail'),
       (8, 0, 'panther', 'tail',   'lion',    'head',     'panther', 'head',      'cheetah', 'tail'),
       (9, 0, 'cheetah', 'head',   'tiger',   'head',     'panther', 'head',      'lion',    'tail');

Then, rotate each of those pieces so we have all 36 variations:

# rotate (top --> left)
INSERT INTO rotated_pieces (piece_id, rotation, top, top_head, `right`, right_head, bottom, bottom_head, `left`, left_head) 
  SELECT piece_id, rotation+1, 
         `right` AS top, right_head AS top_head, 
         bottom AS `right`, bottom_head AS right_head, 
         `left` AS bottom, left_head AS bottom_head, 
         top AS `left`, top_head AS left_head 
    FROM rotated_pieces;

# rotate (top --> bottom, left --> right) 
INSERT INTO rotated_pieces (piece_id, rotation, top, top_head, `right`, right_head, bottom, bottom_head, `left`, left_head) 
  SELECT piece_id, rotation+2, 
         bottom AS top, bottom_head AS top_head, 
         `left` AS `right`, left_head AS right_head, 
         top AS bottom, top_head AS bottom_head, 
         `right` AS `left`, right_head AS left_head 
    FROM rotated_pieces;

Then, it's a pretty simple query to return the result. The code is messed up by the WHERE clause which ensures that each piece is used only once in the solution, if there's a neater way to do this I'd like to know.

# find solution
SELECT A.piece_id AS A, A.rotation AS Ar, B.piece_id AS B, B.rotation AS Br, C.piece_id AS C, C.rotation AS Cr,
       D.piece_id AS D, D.rotation AS Dr, E.piece_id AS E, E.rotation AS Er, F.piece_id AS F, F.rotation AS Fr,
       G.piece_id AS G, G.rotation AS Gr, H.piece_id AS H, H.rotation AS Hr, I.piece_id AS I, I.rotation AS Ir
  FROM rotated_pieces A 
INNER JOIN rotated_pieces B ON (A.`right` = B.`left` AND A.right_head != B.left_head)
INNER JOIN rotated_pieces C ON (B.`right` = C.`left` AND B.right_head != C.left_head)
INNER JOIN rotated_pieces D ON (A.bottom = D.top     AND A.bottom_head != D.top_head)
INNER JOIN rotated_pieces E ON (D.`right` = E.`left` AND D.right_head != E.left_head
                                AND B.bottom = E.top AND B.bottom_head != E.top_head)
INNER JOIN rotated_pieces F ON (E.`right` = F.`left` AND E.right_head != F.left_head
                                AND C.bottom = F.top AND C.bottom_head != F.top_head)
INNER JOIN rotated_pieces G ON (D.bottom = G.top     AND D.bottom_head != G.top_head)
INNER JOIN rotated_pieces H ON (G.`right` = H.`left` AND G.right_head != H.left_head
                                AND E.bottom = H.top AND E.bottom_head != H.top_head)
INNER JOIN rotated_pieces I ON (H.`right` = I.`left` AND H.right_head != I.left_head
                                AND F.bottom = I.top AND F.bottom_head != I.top_head)
WHERE A.piece_id != B.piece_id AND A.piece_id != C.piece_id AND A.piece_id != D.piece_id
  AND A.piece_id != E.piece_id AND A.piece_id != F.piece_id AND A.piece_id != G.piece_id
  AND A.piece_id != H.piece_id AND A.piece_id != I.piece_id
  AND B.piece_id != C.piece_id AND B.piece_id != D.piece_id AND B.piece_id != E.piece_id
  AND B.piece_id != F.piece_id AND B.piece_id != G.piece_id AND B.piece_id != H.piece_id
  AND B.piece_id != I.piece_id AND C.piece_id != D.piece_id AND C.piece_id != E.piece_id
  AND C.piece_id != F.piece_id AND C.piece_id != G.piece_id AND C.piece_id != H.piece_id
  AND C.piece_id != I.piece_id
  AND D.piece_id != E.piece_id AND D.piece_id != F.piece_id AND D.piece_id != G.piece_id
  AND D.piece_id != H.piece_id AND D.piece_id != I.piece_id
  AND E.piece_id != F.piece_id AND E.piece_id != G.piece_id AND E.piece_id != H.piece_id
  AND E.piece_id != I.piece_id
  AND F.piece_id != G.piece_id AND F.piece_id != H.piece_id AND F.piece_id != I.piece_id
  AND G.piece_id != H.piece_id AND G.piece_id != I.piece_id
  AND H.piece_id != I.piece_id;

This returns 4 rows, the first is our solution:

2,1, 1,0, 6,0, 
8,3, 9,3, 7,2, 
5,3, 3,0, 4,0

The remaining 3 are the same solution with the entire board rotated:

6,1, 7,3, 4,1, 
1,1, 9,0, 3,1, 
2,2, 8,0, 5,0

4,2, 3,2, 5,1, 
7,0, 9,1, 8,1, 
6,2, 1,2, 2,3

5,2, 8,2, 2,0, 
3,3, 9,2, 1,3, 
4,3, 7,1, 6,3

I would argue the SQL version is clearer, but the Prolog version is slightly terser and should be easier to extend to a 4x4 puzzle.

Monday, February 21, 2011

Merrell Trail Gloves review

A quick review of the Merrell Trail Glove minimalist trail running shoes.

After reading a few reviews of these shoes I was curious enough to fork out the money for them (Barefoot Running University and I run far.)

On your foot

The fit is snug in the heel and the laces really grip around the mid-foot and arch. I only have the Vibram FiveFingers and walking shoes to compare to, maybe regular running shoes are more like this too, but they are different to anything I have run in before. The toes have tonnes of room and are completely free to move, but don't slide around at all because the midfoot is held solidly.

They breath pretty well, a lot of the upper is mesh/fabric, the tongue's a bit thick so they're pretty warm but regular shoes+socks would be worse.

They let a bit of dirt in around the ankle and I think through the mesh/fabric (I wear them without socks), but because it can move around inside the shoe easily I don't think it's as annoying as when wearing Vibrams.

Most of my shoes are 43 or 44 (Vibrams are 42), and I got the Merrells in size 44 to make sure my toes would have room to splay and wouldn't hit the end heading downhill. This was a bit of a mistake, the way they fit means you won't slide inside them and the bit of extra length means I catch my toe on steps a bit more than I'd like. Not a show stopper, I just have to lift my feet higher.

On the ground

The sole has a bit of cushioning and feels a little soft on the footpath, but you don't notice it as soon as you get off road. It's much thicker and stiffer than the Vibrams, with less ground feel, but still very flexible and nothing like a normal running/trail shoe.

The tread has maybe 2-3mm deep chunks, bigger on the toe and heel. It's very curved and foot shaped, no attempts to control side ways stability or pronation, which is great and they are a tiny bit lighter than Vibram KSOs.

They feel bombproof, no fear treading on rocks/stones/sticks and I'm definitely not going to catch my little toe again like I did wearing Vibrams.

Conclusion

So, these are pretty much exactly what I've been waiting for. I'll only wear them on rough/rocky trails, and they're not "barefoot" by any stretch of the imagination, but they are great minimal shoes and the biggest problem I can see is that I'm going to be exhausted, the Vibrams give you an excuse to slow down occasionally "to negotiate tricky terrain" :)


Dandenong Ranges

Friday, February 04, 2011

LED Bike Light update

My home built LED bike light performed well for many years, but when I got my new bike, the battery holder and handlebar mounting solutions no longer functioned adequately so it was time for a redesign.

I had been planning on a new simplified version, eliminating the micro-controller and putting in a simple dimmer potentiometer. It turned out I never really used the battery monitoring features and simply used a multimeter to decide when to recharge, and using a dimmer would allow setting the brightness easily and quickly and also allow the "high beam" mode to remain on through hazardous sections when necessary.

I was still pretty keen on building this myself as all it really required was cannibalising my old light for parts and making a new case. Commercial solutions had dropped a lot in price but are still many times more expensive than DIY (a few friends have these AYUP lights for example and they seem great).

Then I discovered semi-cheap LED torches as an option and ended up buying a Fenix LD20 LED torch and a small clamp from Ebay (that I can't find again, glad I bought 2...)

This puts out almost exactly the same amount of light as my old version but is in a small waterproof and easy to use package. As a bonus, it's a torch I can take camping etc. too.

I'm simply rotating through the NiMH AA batteries from the old light and am a bit disappointed with the battery life, seeming to have to change them more often than I'd like, but that's pretty quick and an extra set of batteries isn't too much of a struggle to carry around.

All in all, for $80 you can't go too wrong.

Thursday, December 16, 2010

Preparing an audiobook for listening on an mp3 player

I recently ripped a 7-CD audiobook to listen to on my Sansa Clip+ MP3 player. After a bit of fiddling around with bash I was able to end up with a collection of 183 chapters named 1_01.mp3 through to 7_26.mp3 (the first bit was the disc number, then the track number.)

Simply copying these onto my player didn't work as the tracks were all out of sequence. In the past, with smaller books, I've simply skipped around between the sections to listen in order, but that wasn't going to be an option here.

After some experimenting I discovered the Sansa orders the tracks by the Track Number in the ID3 tag, so I fired off the following bash command to set up the ID3 details appropriately:

n=0 ;
for f in *.mp3 ;
do n=$((n+1)) ;
id3tag --artist="author name" --album="book name" --song="${f/.mp3/}" --track=$n $f ;
done

This numbers all the tracks from 1 to 183 in order and also sets the artist and album so I can find it easily in the Audiobook list. The .mp3 is stripped from the displayed filename too.

The id3tag program came from the libid3-3.8.3-dev package, not sure why/when I installed that one...

Saturday, December 04, 2010

Python+Emacs made easy with emacs-for-python

I don't know where this has been all my life but Gabriele Lanaro has put together a really easy to use package for adding lots of Python goodies to Emacs.

Just unzip the archive into ~/.emacs.d/ and add one line to your .emacs and your done (well, also remove all the now unnecessary random cargo-cult prior additions to your .emacs):

(load-file "~/.emacs.d/emacs-for-python/epy-init.el")

I had also previously installed a handful of Python/Emacs packages via Synaptic that may or may not be required to make it all work: python-rope, python-ropemacs, pyflakes.

Friday, July 16, 2010

Increasing podcast tempo (playback speed) with Mplayer and Lame

The following commands will re-encode an mp3 file at a faster speed without increasing the pitch and making it sound like a chipmunk. How this actually works is pretty neat.

mplayer -vo null -vc null \
        -speed 1.33 \
        -af scaletempo,volume=0,resample=44100:0:1 \
        -ao pcm:fast:waveheader:file=temp.wav \
        source.mp3
lame -b 64 --resample 22.05 temp.wav faster.mp3

It will also set the bitrate to 64kbit which was for compatibility with my old player and to reduce the filesize for some podcasts which are unnecessarily big.

There should be a way to use mkfifo instead of temp.wav, and run the mplayer process and lame at the same time, I'll update this when I figure it out.

Update: The above method broke after upgrading to Ubuntu 11.10 (one or both of mplayer or lame changed something I suspect...), so I swapped it out for the simpler and better SoX:

sox --show-progress --norm source.mp3 dest.mp3 \
    tempo -s 1.33 channels 1 rate 22050

This version is easier to understand, faster, doesn't leave need a temp file and also, as a bonus, normalizes the audio to prevent clipping and not hurt my ears.

Using Sansa Clip+ MP3 Player for Podcasts

A mini-review after buying the 2GB model to replace my old mp3 player. I'll only consider it for the purpose of listening to podcasts, other music related features can be found on the SanDisk site or other reviews easily enough.

3 key features for listening to podcasts:

  • No software required, just plug in as a USB drive and copy files across (using a script or manually.)
  • Ability to easily delete files from the player after listening to them.
  • Increase the tempo/speed of playback.

The Sansa Clip+ does all of these, however the fast playback speed is pretty useless as the pitch is increased giving "chipmunk" effects. See the next post for a work-around.

The player remembers the position in each track, which is very nice.

A couple of other issues I've noticed immediately are that it is very small, making one-handed operation difficult, and it has no "hold" button to prevent inadvertent bumping of the buttons. Hopefully being able to clip it outside clothing/bags will alleviate this, time will tell.

It also has a built in battery so you can't carry a spare and must plug it in to charge.

Update: So far, everything is working well. I'm not used to the built in battery, and waiting for it to recharge if I let it go flat is a major pain, but I'll just have to keep it topped up and an hour of charging goes a long way.

One other minor annoyance is that you can't quickly see the duration of a podcast when skipping through them.

Update: One issue that I initially didn't notice, as the ID3 tags were stripped during my reencode process, is that the podcasts are organised by show and there is no "play all" option. This is a pain, I'm used to just listening to them in a more or less random order and not having to stop and select a new show when one finishes.

To quickly fix this, use the id3v2 utility to delete all ID3 tags from the podcast before copying them to the player. This way they are all categorised under a single "Unknown" show menu and the filename is usually sufficient to identify the program.

Wednesday, May 12, 2010

Making use of all the space on a hard drive

If you have a big data drive, there's a good chance that Linux is reserving 5% of it's available space for "emergencies".

This is necessary for your root filesystem as if the disk fills up there may be no way for root to log in and clean things up without this buffer of space for log files etc. to be written into.

However, I have a couple of large drives that store only data and 5% is 100GB I'd rather be able to use.

Fortunately, it's easy to reclaim the space, the following will remove all "reserved blocks":

sudo tune2fs -m 0 /dev/sdXX

This can be reset back to the default 5% (or any percentage) if you need to:

sudo tune2fs -m 5 /dev/sdXX

Friday, January 22, 2010

iPhone woes

My iPhone 3GS died 2 days into our holiday. Something like an IMEI/ICCID not found error, and the only thing it would display was a screen asking to be connected to iTunes to be restored.

Connecting to iTunes wouldn't let me restore as it said my phone was locked with a passcode. Using the forced restore mode, iTunes wouldn't restore without an Internet connection. Once I got a connection, the restore failed with an "Error 23" and the log file contained "radio" errors. Didn't sound good. Tried a DFU mode restore, same "Error 23".

So, it was bricked. My last backup was in October. My bad I guess, but I can only use iTunes via my partner's laptop and usually I don't need to. Thankfully it was still covered by the 1 year warranty.

Calling Apple went smoothly. They acknowledged that I'd done everything I could and booked me an appointment to take the phone into the Apple store.

The Apple store is crazy, there were more staff than customers and the Genius bar was booked out for 2 days. That's where your extra $ go when you buy a Mac, buying blue t-shirts...

The Genius replaced my handset with no troubles, which was great.

Restoring from my backup didn't work very well.

My Apps weren't restored at all, only the links to Web Apps I'd saved to the home screen were restored. This is possibly because I hadn't "authorised" iTunes with my account. Hard to recall if I was warned about that, I didn't know it was necessary in any case.

Restoring Apps manually is a pain, trawling through iTunes receipt emails, figuring out which I hadn't uninstalled and which I wanted to keep using.

Additionally, you have to buy the App again, only once that's done does it confirm that you're not going to have to pay for it again (and gives you a cancel button, God knows why.) If you choose the wrong App then bad luck, the one-click purchase will install it with no cancel button.

One tiny bit of good news: Many App's settings were backed up, and the settings were restored after re-purchasing the App. This wasn't clear initially, but obviously it backs up and restores all the files in the "home" folder, whether the App is present or not.

The music wasn't synced at all as I had iTunes set to manage it manually. Fair enough in that case, but still tedious to get it back.

The lesson to be learnt is that an iPhone backup isn't really a backup at all. It apparently backs up your "home" folder and relies on the Sync to do the rest. This is sort of obvious in hindsight, as it's not like there's going to be room for a 32gb backup, and theoretically that's mostly copies of files (music) that's on the computer anyway.

Just because I don't want everything to be automatically Synced doesn't mean I don't want it backed up.

So, I'll be changing my habits from now on. I won't be switching to using iTunes, that's not an option on Linux, but I'll definitely treat the iPhone as a terminal only and try to keep as much as possible "in the cloud". Using Google Sync for my contacts in addition to my mail and calendars from now on for example.

Sunday, September 27, 2009

Canvas Game on iPhone

I just ported my little canvas game to the iPhone:

Go play!