Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Monday, April 22, 2013

Keeping OS X Awake

Wake up and smell the coffee...

OS X 10.8 has quite an agressive sleep strategy. Quite often, when writing programs for the Enigmatic Code site, I need to stop my laptop sleeping while it runs a program to generate a solution to one of the problems.

I found out last year about the caffeinate command in OS X, which stops the system from sleeping while a command is running. So I often use something like this:
time caffeinate pypy enigma82.py 6

if I want to leave my laptop running something when I'm out, or overnight (and know how long it took).

But what happens if a program has been running for a while and you forgot caffeinate it before you started? You don't want to kill it off and lose all the work it's done, just so you can restart it under caffeinate.

Well the answer is simple, just write a quick Python¹ program to monitor the process you are interested in, and caffeinate that instead.

Here's my quick Python program - I called it pid-exists - it just monitors a process (using the pid specified as the command line argument), until it disappears, and then exits itself.
import sys
import os
import time

PID = int(sys.argv[1])

while True:
  try:
    os.kill(PID, 0)
  except OSError:
    break
  time.sleep(10)

Then you caffeinate that instead. For example:
caffeinate pid-exists 57905

will stop the laptop from sleeping while the process while pid 57905 is active. Once the process 57905 terminates then so will the pid-exists process and the parent caffeinate process and the laptop will go back to it's normal sleep strategy.

Note: On OS X 10.11 you can now just use:
caffeinate -w <pid>
to keep the computer awake while process <pid> is running.

───
¹ Other scripting languages are available.

Wednesday, April 10, 2013

Sliding Puzzle in Python

As part of making a constructive solution to Enigma 1444 I wrote a some Python code to capture a simple "sliding puzzle" solving algorithm. (You can see my code on the Enigmatic Code site).

This is all very well, but I wanted to see the algorithm in action. So instead of cutting up pieces of paper and sliding them around my desk, I wrote a Tk app using Python.


You need to specify the puzzle dimensions on the command line when you start the program. In this case for a 5×5 grid I used:
python sliding-puzzle.py 5 5

A window should show up with the puzzle in it, and you can interact with it by clicking on a tile (adjacent to the blank square) and it should move appropriately. It will keep track of the number of moves and the elapsed time, and if you select an appropriate Target from the drop-down menu it will tell you when you achieve it as a solution. You can choose from "Normal", which is the tiles in their "natural" position, or "Reversed", which is the tiles in reverse order. Note that it is not possible to solve a grid into the "Reversed" configuration for all puzzle dimensions, but that's what the Enigma is about, so that's the default.

There's a button marked Scramble. If you press that the grid will be placed into a random (but not impossible to reach from the initial position) configuration.

There's also a button marked Solve, which if you click it will start solving the puzzle using the algorithm I wrote for the Enigma puzzle.

When you click Solve the puzzle shows you the piece it is currently trying to place (by highlighting the piece in yellow), and also the position it is trying to place it in (by placing a yellow highlighted border around it). Once a piece is placed it is slightly greyed out. Also the Solve button changes to a Stop button, should you wish to abandon the automated solution.

If the target configuration is not possible, the message area will display Impossible Target, and the app will beep.

I used this to demonstrate that the smallest solution for Enigma 1444 - a 10×5 puzzle - is indeed possible in less than 15 minutes, and this is the configuration that the program is set to solve by default. Just fire it up, hit Solve and sit back and enjoy the show. It takes 1300 moves and on my machine it runs in about 4 minutes.

The default animation speeds mean you would have to pretty nimble-fingered to keep up with the program in real life (or use a cleverer algorithm to solve puzzles in fewer moves), as it makes around 5 moves/s. (There are command line parameters if you want to change the speed of moves). I manually reversed a 5×5 puzzle in 3 minutes (three times slower than my program), so a 10×5 puzzle should certainly be possible in under 15 minutes by hand. (Update: I reversed the 10×5 puzzle manually using this program in 824 moves, in 10m27s).

The next smallest solutions are also solved in less than 15 minutes by the program (but only just). A 14×7 puzzle is solved in 3828 moves, and a 11×10 puzzle is solved in 4122 moves. These both take about 14 minutes. After that a 22×5 solution is solved in 6344 moves and takes about 21 minutes.

If you like you can specify a target configuration on the command line. For example you can try to solve the puzzle for which Sam Lloyd offered $1,000 by running:
python sliding-puzzle.py 4 4 1 2 3 4 5 6 7 8 9 10 11 12 13 15 14

and then clicking Solve - but you'll find the solution is impossible.

There's still a few rough edges in the program, and it's not particularly efficient, but it's works sufficiently well for me to see my solution to Enigma 1444 working, so I'm unlikely to take it further.

I've tested it on Mac OS X 10.8.3 under Python 2.7.4 and Python 3.3.1 with Tk 8.5.9, and on Linux (Ubuntu 12.04.2) under Python 2.7.3 and Python 3.2.3.

Tuesday, January 10, 2012

Enigmatic Python

While attempting to find a programmatic solution to Enigma 1602 (which really is much easier to do with pencil and paper) for the Enigmatic Code Blog, I briefly toyed with the idea of writing my own code to solve sets of linear simultaneous equations.

Then I discovered the rather marvellous SymPy library, which can do that and much more, and made for a very neat solution.

I've really only scratched the surface of this module, but I expect to use it more in the future.

Along with other Python modules that aid in the writing Enigma solutions: unlimited precision integers, set, itertools, collections, fractions, probably lots of other standard modules I have yet to discover, and, of course, my own set of useful routines.

And finally, to squeeze that bit extra out your Python programs you can always try using PyPy, which is a Python interpreter, written in Python. It includes a JIT compiler and often ends up running faster than the standard CPython interpreter.

I tried it on my code for Enigma 1653 - one of the trickier ones - and I got the following runtimes:

CPython 2.7.1: 2m01s
CPython 3.2: 1m52s
PyPy: 0m10s

Monday, September 26, 2011

Dice Emulator

The Merry Game of Floundering
The other day Caroline came home with a charity shop find - The Merry Game of Floundering* - which I'm sure we used to play as kids (although my Mum claims never to have heard of it).

All the pieces were there with the exception of one of the dice (it needs to two). So rather than search for another die I did what any self-respecting programmer would do, and knocked up a dice emulator in Python.

Thanks to the pygame library and Unicode characters U+2680 - U+2685 I was able to get a program together quickly which we then triggered using the Apple Remote (using Remote Buddy's Virtual Mouse behaviour). Simple as that!

dice.py
The program is presented below. Be aware, though, it makes some assumptions about running on my MacBook. If you're running it on non-1280x800 screen size you'll probably need to change the font sizes, and if you're running on a non-Apple system you'll probably need to change the font for the die characters (or go back to using ASCII numbers, as it was originally), and the location of the sound that is played when the dice is rolled. Also there is some code there to highlight special scores (doubles, and 6 and 1), which are specific to the Floundering game - take them out, or define your own. Enjoy!



[*] While searching for more information on the game I found someone selling a set on eBay as "The Messy Game of Floundering".

Friday, July 31, 2009

Scripting OS X

One of the nice things about Mac OS X is that you can interact with many of the applications using external scripts. The main issue is what scripting language you use to write your scripts in. The obvious choice is, of course, AppleScript, but while it makes it easy to interact with the applications it isn't as functional at text or date manipulation as a traditional scripting language, such as Perl.

A few years ago I was pleased to find there was a Perl module available called Mac::Glue that would let you talk to applications in OS X without having to use AppleScript. I had downloaded the AppleScript guide from Apple and had tried to use it, honest. But having used Perl nearly every waking hour over the previous 7 years I had a fair amount of expertise locked up in Perl and in a short time, with the help of Mac::Glue, I was able to knock up a quick script to help me organise my iPhoto library, and later I added scripts that I used with other applications.

Perl, however, is not an officially supported scripting language for OS X, and in OS X 10.5 both Ruby and Python were supported by Apple for scripting OS X. I decided that Ruby was the cooler of the two languages to use as it's a bit like Perl rewritten by a Smalltalk geek (that and I have always had a reservation about Python's use of syntactic whitespace), so in 2008 I dabbled a bit by rewriting my iPhoto script in Ruby using RubyOSA and was pleasantly suprised to find that it ran quite a lot faster.

That was all fine and dandy, and I transitioned from OS X 10.4 on my PowerBook G4 to OS X 10.5 on my MacBook without a glitch. Until Apple released OS X 10.5.7, at which point my RubyOSA scripts stopped working, so I went back to using Perl and Mac::Glue.

Recently I have wanted to export playlists from iTunes to the memory stick from my phone (or sometimes to a USB stick or just to a directory), something that iTunes doesn't seem that keen on (unless optical media is involved). And I have also been looking for a suitable programming language for my 10 year old nephew (who has just got a netbook for his birthday - Hi, Matthew!) to learn. So, bolstered by a comment on Slashdot that had mentioned it only takes a couple of hours to learn, I put away my irrational dislike of Python and decided to give it a go. And sure enough I was able to knock up a script, very straightforwardly, that did exactly what I wanted.

If you're a UNIX command line geek and you'd like to give it a go you can download from the link below. Obviously you'll need the Python appscript library for it to work.
The script currently supports the following actions:
  • list-playlists [<pattern>]
    This lists the playlists in iTunes (that optionally match the specified pattern). The playlists listed include the names of the enclosing folders, and are prefixed by an integer index for easy reference (especially useful if you have multiple playlists with the same name). Also displayed is a track count, the duration of the playlist and the cumulative size of the files in the playlist.
  • export-playlist <playlist> [<dir>]
    This exports media from the specified playlist to specified directory (or the current directory if none is specified). You can specify the playlist either as the playlist name (along with enclosing folders) as, or the playlist index, both of which are printed out by list-playlists. The directory will be created, if it doesn't exist. The filenames for the exported media are generated from the track numbers in the playlist, along with the track name, and have punctuation and spaces removed or translated to make them more friendly.
  • export-current-playlist [<dir>]
    If you are currently listening to something in iTunes this will export the current playlist to the specified directory (or the current directory if none is specified).
  • help
    List the available actions, along with brief descriptions.
So you can use it like this:
% itunes list-playlists never
[282] CDs > Nirvana > Nevermind (12trk 42m31s 61.4MB)
[547] Compilations > 16. Never Give In (2008) (28trk 1h33m15s 127.4MB)
% itunes export-playlist 547 /Volumes/JIM\'S\ W380I/music/compilations/never_give_in
Exporting: "Compilations > 16. Never Give In (2008)" -> /Volumes/JIM'S W380I/music/compilations/never_give_in
Creating directory: /Volumes/JIM'S W380I/music/compilations/never_give_in
[ 1] "Changed Daily" -> 01-changed_daily.mp3
[ 2] "It's A Fine Day" -> 02-its_a_fine_day.mp3
[ 3] "Road To Nowhere" -> 03-road_to_nowhere.mp3
[ 4] "To Get Down" -> 04-to_get_down.mp3
[ 5] "Teenage Dirtbag" -> 05-teenage_dirtbag.mp3
[ 6] "Gay Bar" -> 06-gay_bar.mp3
[ 7] "Voodoo Child" -> 07-voodoo_child.mp3
[ 8] "Games Without Frontiers" -> 08-games_without_frontiers.mp3
[ 9] "Overload (Original Edit)" -> 09-overload.mp3
[10] "Best Of You" -> 10-best_of_you.mp3
[11] "19-2000 (Soulchild Remix)" -> 11-19-2000.mp3
[12] "Come On Home" -> 12-come_on_home.mp3
[13] "National Express" -> 13-national_express.mp3
[14] "Some Girls" -> 14-some_girls.mp3
[15] "Turning Japanese" -> 15-turning_japanese.mp3
[16] "All The Small Things" -> 16-all_the_small_things.mp3
[17] "Flagpole Sitta" -> 17-flagpole_sitta.mp3
[18] "Sk8er Boi" -> 18-sk8er_boi.mp3
[19] "Get Over It" -> 19-get_over_it.mp3
[20] "Thrillseeker" -> 20-thrillseeker.mp3
[21] "Single Girl" -> 21-single_girl.mp3
[22] "Sale Of The Century" -> 22-sale_of_the_century.mp3
[23] "P.V.C." -> 23-pvc.mp3
[24] "Take Me Out" -> 24-take_me_out.mp3
[25] "Scream" -> 25-scream.mp3
[26] "Underwater Love" -> 26-underwater_love.mp3
[27] "Green Bird" -> 27-green_bird.mp3
[28] "Changed Pandimensionally" -> 28-changed_pandimensionally.mp3

Note that I have a symlink to itunes from itunes.py, so I can point the itunes command to whichever implementation of the script is currently in favour.

If you don't like the " > " separator used to indicate playlist folders you can change the SEP variable in the script to whatever you prefer.

One day I may learn to do GUI scripting in OS X and put an interface on to it.

The only problem I did have was with Unicode characters. Although my terminal is set to use UTF-8 and the $LANG variable is set accordingly, Python kept blowing up when it encountered playlists or folders with non-ASCII characters in. So I did a bit of jiggery-pokery in the script that seemed to sort it out for me. When I am more familiar with Python I may come up with a better solution.

Feel free to use this script, and also to pass any comments on to me, although it works for me as I intended so I'm unlikely to put a great deal of effort into maintenance. Bear in mind it is my first Python script (and I happen to like 2 space indents). I'm hoping that it will continue to work when OS X 10.6 (Snow Leopard) is released in September (and even OS X 10.5.8 which is likely to come even sooner).