Skip to content

Category Archives: Programming

Python Client for Omegle

24-Feb-10

Last weekend I wrote some Python bindings for Omegle and proceeded to write a variety of clients. The first real client I wrote was a multi-user chat room for Omegle. The script starts a few chats, and forwards messages from each to all of the others, prefixing them with "#N#: " (where N is a number assigned to the sender). Next, I made a nicer version of a client which allows one to spy on a conversation between two other users, or interject however one wants. Finally, I wrote a basic Omegle client, which I'll be uploading here.

Though the client is fully usable, it does not yet pass along reCaptcha requests to the user, and I might go back and change how I did the threading. The UI uses curses, only because I wanted some experience with it. I'll definitely post any revisions I do, and I might post the chat room client at some point. I don't think I'll post the spy client. If you have any programming experience, you can build one yourself.

For your enjoyment, a Python Omegle Client.

Senior Project, Application Launcher, System Customizations

11-Jan-10

It has been a while since my last update, and since then we have gotten completely usable two-glove audio output, without accelerometer-based articulations. It is very cool (we can kind of play the Tetris A Theme, but no one is very good at using the gloves yet). Just today we got the accelerometer all working; we have rotation and vertical motion output fit to the correct ranges. Despite having the software all written, we don't have the gloves working with full articulations due to the issue of actually mounting the accelerometers on the gloves and the Arduinos on the wristbands. We have already bought most of the components we'll need to do this, so I expect to have the entire thing working by the end of this weekend. When we finish, I'll post the final parts list, pictures, and latest code, along with instructions for constructing your own pair of musical gloves as a DIY project. Next!

I haven't been very motivated to finish it since the current version works fine, but I'm almost done with a re-write of my application launcher. The new version will by extensible via plugins and will be able to run in the background (as opposed to starting up every time you summon it). Aside from the rewrite, I added some neat shorthand directory launching support, which allows me to open "/home/ifx/Code/Launcher/final" by typing "/lau/f" and pressing enter. Regex is pretty neat.

Finally, I think I'm going to make a post about all the interesting customizations I have made to my Arch Linux install. These would include my mouse gestures (I have some that actually do useful, non-obvious things), my Conky configuration, and, of course, my .bashrc.

Things That Have Happened, Things That Have Not, and Senior Project Status

12-Nov-09

As of the time of posting, I have ten Google Wave invites. I've been playing with Wave a bit, and it is pretty neat, but not extremely impressive. I have seen real-time collaborative document editing before. Using Wave for chat is fairly cumbersome (no send on Enter). I have also gotten a few crashes, and it isn't incredibly snappy.

All that aside, it is very cool and I do see how it can be useful. Even if just discussing a random topic, the fact that you can reply to related comments and have the whole discussion appear in a tree promotes organization. I am using a neat LaTeX extension that allows me to embed editable LaTeX markup which is automatically rendered and displayed inline. Related to this, I am using Wave with the math teacher to plan for my third trimester class "Seminar in Independent Mathematical Research" (I'm very excited, I might post about my topic ideas later).

I started learning Go recently. For practice, I'm re-writing my Python solutions for Project Euler in Go. Here is Problem 259:

package main
 
import (
    "fmt";
    "strconv";
    "strings";
    "./fraction";
    "time";
)
 
func simplify(nums []*fraction.Fraction) map[string]*fraction.Fraction {
	out := make(map[string]*fraction.Fraction);
	var r *fraction.Fraction;
	if len(nums) == 1 {
		out[nums[0].String()] = nums[0];
		return out;
	}
	for offset := 1; offset < len(nums); offset++ {
		lefts := simplify(nums[0:offset]);
		rights := simplify(nums[offset:len(nums)]);
		for _, left := range lefts {
			for _, right := range rights {
				r = left.Add(right);
				out[r.String()] = r;
				r = left.Sub(right);
				out[r.String()] = r;
				r = left.Mul(right);
				out[r.String()] = r;
				if right.IsZero() != true {
					r = left.Quo(right);
					out[r.String()] = r;
				}
			}
		}
	}
	return out;
}
 
func groups(symbols string) ([]string, int) {
	out := new([512]string);
	out[0] = symbols;
	c := 1;
	for i := 1; i < len(symbols); i++ {
		gout, gcount := groups(symbols[i:len(symbols)]);
		for g := 0; g < gcount; g++ {
			group := gout[g];
			out[c] = symbols[0:i] + "|" + group;
			c += 1;
		}
	}
	return out, c
}
 
func numlist(s string) []*fraction.Fraction {
	vals := strings.Split(s, "|", 0);
	out := make([]*fraction.Fraction, len(vals));
	for i := 0; i < len(vals); i++ {
		t, _ := strconv.Atoi64(vals[i]);
		out[i] = fraction.New(t, 1);
	}
	return out;
}
 
func simplify_nums(nums string) map[string]*fraction.Fraction {
	return simplify(numlist(nums));
}
 
/*
func simplify_nums(nums string, out chan map[string]*fraction.Fraction) {
	out <- simplify(numlist(nums));
}
*/
func main() {
	start := time.Seconds();
	reachable := make(map[uint64]bool);
	seqs, nseqs := groups("123456789");
	for i := 0; i < nseqs; i++ {
		reps := simplify_nums(seqs[i]);
		for _, k := range reps {
			if k.IsPos() && k.IsInt() {
				reachable[uint64(k.Num())] = false;
			}
		}
	}
	/*
	channels := make([]chan map[string]*fraction.Fraction, nseqs);
	for i := 0; i < nseqs; i++ {
		channels[i] = make(chan map[string]*fraction.Fraction);
		go simplify_nums(seqs[i], channels[i]);
	}
	for i := 0; i < nseqs; i++ {
		reps := <-channels[i];
		for _, k := range reps {
			if k.IsPos() && k.IsInt() {
				reachable[uint64(k.Num())] = false;
			}
		}
	}
	*/
	var total uint64 = 0;
	for k, _ := range reachable {
		total += k;
	}
	fmt.Printf("Sum: %d\n", total);
	fmt.Printf("Total Time: %d\n", time.Seconds() - start);
}

If anyone wants "fraction.go", I can upload it.
I tried using goroutines (commented in the code above) to speed things up via concurrency, but it didn't seem to have any effect on the speed (though memory usage increased) and CPU usage only spiked on one core. Not sure what the deal is with that; I'll look into it later.

So far as I have played with it, I like the language. Go has a respectable standard library, which is something many new programming languages (that I have seen) lack. If I code anything interesting enough, I'll make a post about it.

About my Application Launcher: I'll be splitting it up into a daemon process and a GUI process which will communicate using D-Bus (probably, haven't decided yet). I will be adding filesystem searching using Glimpse and locate. Glimpse is pretty damn impressive. It provides very fast searching of file contents, support for fuzzy matching, and the index is quite small (for everything under my home directory the index is ~34 MB).

Related to this, I am going to be looking into Fuzzy Matching once more (see music on the command line). Already existing for Python, I found this. Haven't tested it yet. I downloaded a few papers on Damerau-Levenshtein distance and Levenshtein automata, but probably won't start implementing anything for the next week or two.

Finally, my Senior Project. By the end of this trimester (~1 week) Nate and I plan to have audible note output using data from the contact sensors. We will integrate the accelerometers next trimester, and should have plenty of time to debug and test afterwards. At the moment, though, due to a combination of college applications, homework, and sickness, we are a bit rushed. I don't expect it to be an issue: a good, solid work day this weekend should be enough to catch up.

As a last note, I'll mention the album I've been listening to while writing this up: "Suzumiya Haruhi no Gensou". Orchestral versions of many songs from Suzumiya Haruhi no Yuutsu. Surprisingly good.

Bash, Senior Project, and Application Launcher

24-Oct-09

A while ago I installed a bunch of lib32-* packages as optional dependencies for something. I ended up not needing them for whatever reason (don't remember anymore), so I decided to remove them. The problem was that I also wanted to keep the lib32-* packages that I was actually using. Now, I had my pacman install logs, so I could easily get a list of the specific packages I wanted to remove. If I wanted to be a bit fancier, I could have python parse a section of the logs to generate a remove command to use. This wasn't good enough, though. I decided that I wanted to use a bash one-liner.

After a few hours, I changed my mind. Instead, I have this 7-liner:

TMP1=`mktemp`
TMP2=`mktemp`
yaourt --textonly -Ss lib32-* | grep installed | cut -d "/" -f 2 | cut -d " " -f 1 > $TMP1
yaourt --textonly -Q -t | cut -d "/" -f 2 | cut -d " " -f 1 > $TMP2
comm -12 $TMP1 $TMP2 | xargs yaourt -R
rm $TMP1
rm $TMP2

There might be an easier way, but if you have yaourt installed (this is for Arch Linux users) you can use this little script to remove all the packages matching some search (lib32-* in my case) that aren't required as dependencies for anything (including each other. Run multiple times to get those too.)

Next.

A while ago we received our conductive fabric, and we got nicer output from the accelerometer. It will still need to be "calibrated" at startup to some extent, but this will only require the user to have it not tilted more than 180 degrees from the desired neutral position when the calibration routine is run (which just deals with the first set of outputs from the accelerometer differently from all the others).

And finally:

I have uploaded a working implementation of the application launcher code I wrote a while ago. It doesn't use the panel applet GUI (my end goal), but it does work. Missing features include:

  • "Learning" from previous searches
  • Autodetection of environment (you need to set a few configuration variables manually)
  • Scrolling through results (you can only run the first result at the moment)

All this will be added in time.

Accelerometer Working!

05-Oct-09

Today we got the accelerometer working! We'll need to change how it is set up to get cleaner output, but we do in fact have some output. Also, our conductive fabric (which we had previously been told would be delayed for four weeks) will be here much sooner, though we needed to sacrifice the privilege of using pink fabric for the grey which we are now getting.

Next up is writing the code to use the accelerometer data to produce a rotation and vertical acceleration.

We also decided to document our work with photos, so I'll be uploading photos either to this site or to some photo hosting site at some point.

As a side note, I am also writing a program to use a wacom tablet as instrument with Python + Pygame. Pressure will map to volume, vertical position to frequency, and horizontal position to time until note is played. The interface will be a leftward-scrolling window in which you can draw with the wacom tablet. When whatever you have drawn hits the left side of the window, it will make sound. I'll post more about this later, but if anyone has a good way of getting Wacom Tablet pressure data in Python, feel free to tell me. For some reason reading from /dev/input/wacom (a symlink to whatever /dev/input/event* the tablet is actually mapped to) doesn't work, and I can only get position data using Python's Xlib bindings. It also does not show up as a joystick in Pygame (my laptop's accelerometer does, though), which would have been nice.

Turing Machine Simulator Version 1.0

14-Jan-09

Okay, I wrote a nice little Turing Machine Simulator in Python. All it does right now is simulate, and it won't check to make sure your rules actually work (you can have it shift more than one cell or write whatever symbols you want). I'll be adding more interesting features later (like the ability to look for Busy Beavers / try to see if a turing machine will ever halt).

?View Code PYTHON
from collections import defaultdict
 
def format_tape(tape, pos, width=80):
    left = ""
    right = ""
    center = "|" + str(tape[0]) + "|"
    if pos == 0:
        center = "*" + str(tape[0]) + "*"
    mi = min(tape.keys())
    ma = max(tape.keys())
    for i in xrange(mi, 0):
        if pos == i:
            left += "_" + str(tape[i]) + "_ "
        else:
            left += " " + str(tape[i]) + "  "
    for i in xrange(1, ma + 1):
        if pos == i:
            right += " _" + str(tape[i]) + "_"
        else:
            right += "  " + str(tape[i]) + " "
    return " "*(38 - len(left)) + left + center + right
 
instructions = dict()
 
instructions[(0, 0)] = (1, 1, 1)
instructions[(1, 0)] = (1, -1, 0)
instructions[(2, 0)] = (1, -1, 1)
instructions[(0, 1)] = (1, -1, 2)
instructions[(1, 1)] = (1, 1, 1)
instructions[(2, 1)] = (1, 0, -1)
 
 
#{(state, symbol) : (write, move, state change)}
#move = {-1, 0, 1}
#write = (a number from alphabet) or None (do nothing)
#state change = (a number from states) or -1 (halt) or None (no change)
 
output = int(raw_input("Print tape as we go? (1 or 0): "))
 
tape = defaultdict(int)
 
state = 0
pos = 0
 
while state != -1:
    #Halt if no rule is present
    if not (state, tape[pos]) in instructions:
        print "No rule for (" + str(state) + ", " + str(tape[pos]) + ")"
        break
 
    instruction = instructions[(state, tape[pos])]
 
    #Excecute instruction
    if instruction[0] != None:
        tape[pos] = instruction[0]
    pos += instruction[1]
    if instruction[2] != None:
        state = instruction[2]
 
    if output:
        print format_tape(tape, pos)
print "Halted."

Carpool Planner Version 1.0

13-Jan-09

Okay, I just wrote a neat Python script which uses the Google Maps API to find the optimal setup for a group of people carpooling to a given location. I'll be doing some optimizations and rewriting the entire thing in Javascript soon (to add fancy map images), but this version works.

It is intended for Python 2.6, and you'll need to pickle an empty dictionary to a file named "cache" (no extension) and place it in the same directory as the script for a successful run, along with geopy (which is meant for Python 2.5, but is easy to modify for compatibility).

?View Code PYTHON
from geopy.distance import distance
from itertools import combinations
from geopy import geocoders
from time import sleep
import pickle
import urllib
import json
 
with open("cache", "rb") as f:
    cache = pickle.load(f)
 
def dist(start, end):
    return distance(start[1], end[1]).meters
 
def duration(start, end):
    if not (start[0], end[0]) in cache:
        url = 'http://maps.google.com/maps/nav?key=ABQIAAAAzr2EBOXUKnm_jVnk0OJI7xSosDVG8KKPE1-m51RBrvYughuyMxQ-i1QfUnH94QxWIa6N4U6MouMmBA&amp;q='
        url += 'from:' + start[0].replace(' ', '+') + '+to:' + end[0].replace(' ', '+')
 
        data = json.loads(urllib.urlopen(url).read())
 
        cache[(start[0], end[0])] = data['Directions']['Duration']['seconds']
        with open("cache", "wb") as f:
            pickle.dump(cache, f)
 
        sleep(5)
    return cache[(start[0], end[0])]
 
g = geocoders.Google()
#us = geocoders.GeocoderDotUS()
 
def geocode_wait(loc, geocoder=g):
    if not loc in cache:
        r = geocoder.geocode(loc)
 
        cache[loc] = r
        with open("cache", "wb") as f:
            pickle.dump(cache, f)
 
        sleep(2)
    return cache[loc]
 
def tri(n):
    return (n**2 + n) / 2
 
def selections(mset, k):
    res = set()
    for c in combinations(mset, k):
        res.add(tuple(sorted(c)))
    return res
 
read = str(raw_input("Read data from file? (y/n): "))
print ""
while read not in ['y', 'n']:
    print "I did not understand that. Please respond with 'y' or 'n'."
    read = str(raw_input("Read data from file? (y/n): "))
    print ""
 
if read == 'n':
    print "Please enter your destination."
    dest = str(raw_input("Destination: "))
    print ""
 
    print "Please enter the number of people who need rides."
    need_count = int(raw_input("Rides needed: "))
    print ""
 
    print "Please enter the number of people who can offer rides."
    avail_count = int(raw_input("Rides available: "))
    print ""
 
    need = []
    print "Please enter the locations of the people who need rides."
    for i in xrange(need_count):
        need.append(str(raw_input("Passenger " + str(i + 1) + ": ")))
    print ""
 
    del need_count
 
    avail = []
    slots = []
    print "Please enter the locations of the people who can offer rides and the number of slots available in each vehicle."
    for i in xrange(avail_count):
        avail.append(str(raw_input("Driver " + str(i + 1) + ": ")))
        slots.append(int(raw_input("Slots: ")))
    print ""
 
    del avail_count
else:
    name = str(raw_input("Please enter file name: "))
    print ""
 
    dest = ""
    need = []
    avail = []
    slots = []
 
    print "Reading data from file..."
    with open(name) as f:
        for line in f:
            ldata = line.split(";")
            if ldata[0] == "destination":
                dest = ldata[1]
            elif ldata[0] == "passenger":
                need.append(ldata[1])
            elif ldata[0] == "driver":
                avail.append(ldata[1])
                slots.append(int(ldata[2]))
    print "Done reading data from file.\n"
 
if sum(slots) &lt; len(need):
    exit("Not enough slots available for all passengers. Had " + str(sum(slots)) + " slots, needed a total of " + str(len(need)) + ".")
 
print "Geocoding locations..."
#Destination
dest = g.geocode(dest)
 
#Passengers
need = map(geocode_wait, need)
 
#Drivers
avail = map(geocode_wait, avail)
print "Done Geocoding locations.\n"
 
print "Destination is", dest[0]
print ""
 
for p in xrange(len(need)):
    print "Passenger", p + 1, "is at", need[p][0]
print ""
 
for d in xrange(len(avail)):
    if slots[d] == 1:
        print "Driver", d + 1, "is at", avail[d][0], "and can take", slots[d], "passenger"
    else:
        print "Driver", d + 1, "is at", avail[d][0], "and can take", slots[d], "passengers"
print ""
 
mset = []
for n in xrange(len(slots)):
    mset += [n] * slots[n]
choices = selections(mset, len(need))
del mset
 
def routes(plan, nodes, start=None):
    if not start is None:
        origin = start
    else:
        origin = avail[plan[0]]
    paths = dict()
    for i in xrange(len(nodes)):
        cnode = nodes[i]
        if plan[1:] != tuple():
            if plan[1] == plan[0]:
                ops = routes(plan[1:], nodes[:i] + nodes[i+1:], start=cnode)
            else:
                ops = routes(plan[1:], nodes[:i] + nodes[i+1:])
            for op in ops:
                paths[(cnode, ) + op] = duration(origin, cnode) + ops[op]
        else:
            paths[(cnode, )] = duration(origin, cnode)
    return paths
 
best = None
 
for layout in choices:
    pool = need
    options = routes(layout, pool)
 
    subbest = (min(options), options[min(options)])
    if best is None:
        best = (layout,) + subbest
    if subbest[1] &lt; best[2]:
        best = (layout, ) + subbest
 
last = -1
for g in xrange(len(best[0])):
    if best[0][g] == last:
        print "...then continue on to pick up", best[1][g][0]
    else:
        print "Driver", best[0][g] + 1, "should pick up", best[1][g][0]
    last = best[0][g]
print "This setup should yield a total driving time of", best[2], "seconds"