tomchuk

a little place for me to write things down so I don't forget them

Starting A VMware guest at logon and mounting a network share

So I've been struggling trying to get my development VM started and the Samba share it exports mapped when I boot up. Last time I set this up, I'd just run the VM through vmrun.exe with a shortcut in Start Menu -> Startup. The problem is that on logon, Windows would attempt to mount the share before the VM was started. Also, I'm not entirely sure what happens to a running VM when you shut down Windows without halting/suspending it first, though it never seemed to be an issue.

The following will start the VM and mount the share in non-persistent mode (won't automatically try and re-mount at logon). It will also remove the mount and suspend the VM on logoff, leading to a faster start-up next time.

Fist create two scripts. I dropped them right in the VM's folder.

dev-start.cmd:

@echo off
"C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe" -T ws -u thomas start "D:\vm\Dev\Dev.vmx" nogui
ping 10.0.1.199 -n 1 -w 2000 > NUL
net use Y: \\dev\thomas /persistent:no

dev-stop.cmd:

@echo off
net use Y: /delete /Y
ping 10.0.1.199 -n 1 -w 2000 > NUL
"C:\Program Files (x86)\VMware\VMware Workstation\vmrun.exe" -T ws -u thomas suspend "D:\vm\Dev\Dev.vmx" soft

In both of the scripts, change the drive letter (Y:), the share (\\dev\thomas), the location of the VMX file (D:\vm\Dev\Dev.vmx) and the username (thomas). This of course depends on a share exported from the guest, DNS or static networking+hosts file entry and bridged networking in VMware. It also requires the same credentials for your Windows account and the share, if this isn't the case, take a peek at the net use docs. Here's the vmrun docs (PDF) as well.

Now to setup these scripts to be run at logon and logoff. We'll do this through Group Policy Editor, as it also allows you to set up logoff scripts. Navigate to: Start -> Run -> gpedit.msc -> User Configuration -> Scripts (Logon/Logoff). Double click Logon, click Add and navigate to where you saved your start script. Do the same for Logoff. This will also work in the Computer Configuration section, but I haven't found a way to allow one to bring the running VM to the foreground.

Enjoy your automatically started VM and mounted network share every time you logon to your computer.

Changing the Default Playback Device on Windows 7 (with less clicking)

I use speakers to listen to music while I work during the day, and a headset to play a little Battlefield 3 in the evening. I've been looking to an alternative to opening up the playback devices control panel every time I make the switch. I found that Dave Amenta had done all the hard work of uncovering the undocumented APIs and writing a console application. I did make a couple changes to his code so that the default playback device would be applied across all roles. Here's the modified code.

Download the above and drop Release\EndPointController.exe somewhere on your %PATH% (I chose C:\Windows\System32) and run it from a command prompt. It'll give you a list of devices and their IDs, remember the IDs for the devices you want shortcuts to.

Right-click and drag EndPointController.exe to your desktop and select Create Shortcuts Here. Right-click the shortcut and choose properties and add a space and the device ID to the end of the Target. You can change the icon as well (Grab audio device icons from %SystemRoot%\system32\mmres.dll). You might as well change Run: to Minimized so the console window doesn't flash when you run the shortcut.

I've also set up AutoHotKey (download AutoHotkey_L, not Basic) to change devices with a keyboard shortcut:

; Cycle through playback devices with Ctrl+Alt+S
^!s::
if not TotalEndPoints {
    TotalEndPoints = 0
    RunWait, %comspec% /c EndPointController > %A_Temp%\endpoints.txt,, Hide
    Loop, Read, %A_Temp%\endpoints.txt
        TotalEndPoints++
    FileDelete, %A_Temp%\endpoints.txt
}
CurrentEndPoint++
CurrentEndPoint := Mod(CurrentEndPoint, TotalEndPoints)
RunWait, %comspec% /c EndPointController %CurrentEndPoint% ,, Hide
return

; Switch to playback device 0 with Ctrl+Alt+0
^!0::
RunWait, %comspec% /c EndPointController 0 ,, Hide
return

; Switch to playback device 1 with Ctrl+Alt+2
^!1::
RunWait, %comspec% /c EndPointController 1 ,, Hide
return

; Switch to playback device 2 with Ctrl+Alt+2
^!2::
RunWait, %comspec% /c EndPointController 2 ,, Hide
return

Solving Mazes with Python

I stumbled across the following ridiculous maze on Reddit. It's been a few years since my last AI class, so I decided to brush of the ol' pathfinding algorithms and take a crack at it. It may look like a gray square, but click it --- you'll see how crazy it really is.

Here's the code. You'll need progressbar and Pillow.

#!/usr/bin/env python

import math
import struct
import sys
from collections import deque
from progressbar import ProgressBar
from PIL import Image

class Maze():

    def __init__(self, image_file):
        self.image = Image.open(image_file)
        self.image = self.image.convert("RGB")
        self.w, self.h = self.image.size
        self.maze = self.image.load()

        self.start = self.end = None
        self._last_was_wall = True
        def check_cell(x,y):
            if any(self.maze[x,y]):
                if self._last_was_wall:
                    if not self.start:
                        self.start = (x,y)
                    elif not self.end:
                        self.end = (x,y)
                    else:
                        raise Exception('More than two openings')
                self._last_was_wall = False
            else:
                self._last_was_wall = True
        for y in range(self.h):        check_cell(0,y) # Left
        for x in range(self.w):        check_cell(x,self.h-1) # Bottom
        for y in range(self.h, 0, -1): check_cell(self.w-1,y-1) # Right
        for x in range(self.w, 0, -1): check_cell(x-1,0) # Top
        del self._last_was_wall

        self.search_depth = self.image.convert("1").histogram()[255]

        self.metadata = {}

    def adjacent(self, xy):
        rv = {}
        x,y = xy
        for x1,y1 in [(x,y-1), (x+1,y), (x,y+1), (x-1,y)]:
            try:
                if any(self.maze[x1,y1]):
                    rv[x1,y1] = self.maze[x1,y1]
            except IndexError:
                pass
        return rv

    def geometric_distance(self, c1, c2):
        return math.sqrt(abs(c1[0]-c2[0])**2 + abs(c1[1]-c2[1])**2)

    def get_heuristic(self, cell):
        h = 0
        try:
            x,y,h = struct.unpack('HHH', self.metadata[cell])
        except:
            pass
        return h

    def get_parents(self, cell):
        rv = []
        try:
            x,y,h = struct.unpack('HHH', self.metadata[cell])
        except (KeyError, struct.error):
            return rv

        while True:
            rv.append((x,y))
            try:
                x,y,h = struct.unpack('HHH', self.metadata[x,y])
            except (KeyError, struct.error):
                return rv
        return rv

    def save(self):
        for xy in self.path:
            self.maze[xy] = (255,0,0)
        self.image.save('solution.png')
        print 'Found path of length: %d' % len(self.path)

    def solve_astar(self):

        search_cell = self.start
        open_set = set()
        open_set.update([search_cell])
        closed_set = set()
        self.astar_done = 0

        pbar = ProgressBar().start()
        while len(open_set) > 0:

            if self.astar_done % 100 == 0:
                pbar.update(100.0*(float(len(closed_set))/float(self.search_depth)))
            self.astar_done += 1

            for cell in self.adjacent(search_cell):
                if cell == self.end:
                    x,y = search_cell
                    self.metadata[cell] = struct.pack('HHH', x, y, 0)
                    self.path = self.get_parents(cell)
                    pbar.finish()
                    self.save()
                    return

                if cell not in closed_set and cell not in open_set:
                    open_set.update([cell])
                    if cell not in self.metadata: self.metadata[cell] = []
                    self.metadata[cell] = struct.pack('HHH', search_cell[0],
                        search_cell[1], len(self.get_parents(cell)) + self.geometric_distance(cell, search_cell))

            closed_set.update([search_cell])
            try:
                open_set.remove(search_cell)
            except KeyError:
                pass
            search_cell = None

            for cell in open_set:
                if search_cell is None:
                    search_cell = cell
                    continue
                if self.get_heuristic(search_cell) > self.get_heuristic(cell):
                    search_cell = cell
        print 'No Solution'

    def _bfs(self):
        queue, enqueued = deque([(None, self.start)]), set([self.start])
        while queue:
            parent, n = queue.popleft()
            self.bfs_done += 1
            yield parent, n
            new = set(self.adjacent(n)) - enqueued
            enqueued |= new
            queue.extend([(n, child) for child in new])

    def solve_bfs(self):
        pbar = ProgressBar().start()
        self.bfs_done = 0
        parents = {}
        for parent, child in self._bfs():
            pbar.update(100.0*(float(self.bfs_done)/float(self.search_depth)))
            parents[child] = parent
            if child == self.end:
                revpath = [self.end]
                while True:
                    parent = parents[child]
                    revpath.append(parent)
                    if parent == self.start:
                        break
                    child = parent
                self.path = list(reversed(revpath))
                pbar.finish()
                self.save()
                return
        print 'No Solution'

if __name__ == '__main__':
    try:
        maze = Maze(sys.argv[1])
    except (IndexError, IOError):
        print "Specify file"
        sys.exit(1)
    else:
        if sys.argv[2] == 'b':
            maze.solve_bfs()
        else:
            maze.solve_astar()

Who Steals Tree Sweaters?

So last week some asshole decided to steal the (somewhat) famous tree sweaters from the trees on my block. Not only did the thief make off with Laurie Russell's originals, but he also took the little white "branch sweater" S.B.K. added to the collection a few weeks ago. Not the type to take tree sweater theft lying down, the residents of 16th Street (or maybe just S.B.K.?) have re-clothed the trees.

A Quick and Dirty Blog with Flask and Some Other Bits

I've been eyeing Flask for a while. I've bookmarked dozens of blog posts, github repos and documentation pages in anticipation of playing with Flask. Yesterday, I ran into a post on Reddit entitled, Flask is Awesome for Beginners:

Actually, I don't know if it is. But since everyone else seems to be engaged in “that discussion” again I want to friendly remind you that we (the Flask team) are open to suggestions about how to make things more beginner friendly.

If you have suggestions, leave them here or on the feedback website.

I've done a half-dozen projects in Django, one in Tornado and a couple in web.py. Having never touched Flask before, I sat down yesterday afternoon, and twenty-four hours later, I've got a fully-functional blog in a single file and 460 lines of code. Not to mention, I built in all the cool features I wanted.

Features

OpenID Authentication: I really didn't feel like dealing with another login and auth system (though flask-peewee makes that pretty easy). So with a couple models, a couple views I've got a super simple auth system that lets me (and others) log in using their Google, AOL or Yahoo account.

Markdown Everywhere: Blog posts and comments get run through markdown with a couple of extensions. The first is mdx_video.py which will automatically replace YouTube, Vimeo, etc. links with embedded videos. The second is mdx_pygments.py which will allow me to show you how to fix mdx_video to work with a more recent version of markdown (etree has been moved to util):

def flash_object(url, width, height):
    obj = markdown.util.etree.Element('object')
    obj.set('type', 'application/x-shockwave-flash')
    obj.set('width', width)
    obj.set('height', height)
    obj.set('data', url)
    param = markdown.util.etree.Element('param')
    param.set('name', 'movie')
    param.set('value', url)
    obj.append(param)
    param = markdown.util.etree.Element('param')
    param.set('name', 'allowFullScreen')
    param.set('value', 'true')
    obj.append(param)
    #param = etree.Element('param')
    #param.set('name', 'allowScriptAccess')
    #param.set('value', 'sameDomain')
    #obj.append(param)
    return obj

Drag and Drop Uploads: I hate putting images in blog posts. You've either got some overly strict template or some nasty js editor that spews everything with random markup. So a few lines of js combined with jQuery File Upload, and I can just drag an image (or any other file) onto the page, it will be uploaded and thumbnailed and the proper markdown will be inserted right at my cursor.

Max with my socks