Home
djfroofy
Recent Entries 
5th-Apr-2008 09:50 pm - I has Game
avatar

ロボットゲーム(pyweek6のプロジェクト)

pyglet(又OpenGLとmiruも使い)でほとんど純粋Pythonゲーム〇 アプリケーション

Twistedを使って、ゲームのアクターとSchedulingを制御する。

Links:



Pyweek details:

   http://www.pyweek.org/e/etf/

To play:

  $ hg clone http://hg.enterthefoo.com/roboto Roboto
  $ cd Roboto
  $ python run_game.py (less) 
31st-Jan-2008 08:20 am - The Problem with Doctests
avatar

I used to think python doctests were the greatest thing since sliced bread. I no longer think this and I've returned to the basics (most of the time): plain old unittests. So let me explain why.

This is the crux of the matter: documents are supposed to be documents and readable as such. Too often I see doctests that stray from readability in favor of including chunks of code which achieve testability - test set up, tear down, white space stripping ... For example, let's look at a part of README.txt in zope.event (and the Zope camp definitely embraces doctests):

The package has a list of subscribers.  Application code can manage
subscriptions by manipulating this list.  For the examples here, we'll
save the current contents away and empty the list. We'll restore the
contents when we're done with our examples.

  >>> import zope.event
  >>> old_subscribers = zope.event.subscribers[:]
  >>> del zope.event.subscribers[:]

This isn't documentation! You're wasting time (your time and mine) explaining something to me that has nothing to with how to use the interface. doctest does provide facilities for executing setUp and tearDown functions in regular python modules prior to executing a test, but I seldom see this approach taken. Why? Well I guess this requires more work, and it seems the appeal with doctests is doing less work. I can relate, and even took a crack once at automating specification of setUp/tearDown functions: http://xix.python-hosting.com/file/trunk/xix/utils/test.py .

And while we're on the subject of code I've written, here's another example of a bad doctest:

>>> action.addMessageCategoryCallback(thread1, t1)
>>> action.addMessageCategoryCallback(thread2, t2)
>>> action.addMessageCategoryCallback(metaname, mname)
>>> from loggrok.log import LogStream
>>> stream = LogStream(fname)
>>> stream.messageParser = parser
>>> stream.action = action
>>> for entry in stream:
...     continue
...
====
THREAD1 C
D E
F G
H I
====
THREAD1 J K
====
THREAD2 L M
N
====

WTF? This isn't a document! This is a plain old unittest, disguised as a document! Stop wasting my time!

Okay, so I've been a little harsh. I've tried hard to rant as little as possible on this blog, but today I've fallen short of that goal. But please don't get what I'm saying wrong. I'm not asserting that doctests are useless and you should never use them. (Har har! Get it? "I'm not asserting ...") In fact doctests can be great but I don't think they're effective for fully testing an application - since this will always conflict with writing apt and succinct documentation. What they are good for, if I can suggest, is to verify that examples in a documentation don't contain syntactic errors and are up to date with the current code base. Of course some setUp and tearDown routines may be needed to ensure the examples actually work or don't leave the interpreter in bad state when complete, but there are several approaches to not revealing this in the document (for example, put the setUp/tearDown in comments or use the facilities providing by the doctest library to execute such functions).

On side note, it really isn't worth fussing about, at least on a policy level. If someone on your team is writing unittests (be it doctests or old school unittest), it's best to hold your lip and not engage in bickering about what format they should be in. What matters most is that the application is being tested and the project is approaching good line and combinatorial branch coverage.

29th-Jan-2008 10:23 pm - World Coordinate Picking
avatar

Manipulating an object's world coordinates with OpenGL is tricky. This is an operation which involves translating the screen coordinates to a position in the world. The "tricky" part of this is determining the depth of the object in the range [0,1] to use as the wz argument to gluUnProject (a function that does the hard screen coordinate to world coordinate transformation for us).


Unfortunately, googling for gluUnProject yields some very bad results. For example the NeHe tutorial gives a wretched example using glReadPixels with GL_DEPTH_COMPONENT as the format. This is a really, really stupid idea. First of all, imagine an object is in focus (I've selected it as the target from a drop down menu, or some other way), and then I click on some arbitrary point on the screen. Well, guess what kids? I get the window depth at that point (not the point corresponding to the object I'm interested in). Even if I point directly over the object, it's easy for the mouse to race ahead of the object while dragging it across the screen, and (boom!) the window z depth is 1.0, or the far clipping plane, and my object flies of the friggin' screen. Beautiful!


Other links that crop up literally hard code the wz depth value which only makes sense if you're manipulating an object sitting directly on the near clipping plane.


So, here's how you do it, and it's very simple. To determine the window depth of the object, first do the reverse of gluUnProject (that is, gluProject) based on the coordinates of the object in focus. Here's my final (working) code in python:


        viewport = (GLint * 4)()
        mvmatrix = (GLdouble * 16)()
        projmatrix = (GLdouble * 16)()
        glGetIntegerv(GL_VIEWPORT, viewport)
        glGetDoublev(GL_MODELVIEW_MATRIX, mvmatrix)
        glGetDoublev(GL_PROJECTION_MATRIX, projmatrix)
        wx = GLdouble()
        wy = GLdouble()
        wz = GLdouble()
        sz = GLdouble()
        ex = target.pos.x
        ey = target.pos.y
        ez = target.pos.z
        gluProject(ex, ey, ez, mvmatrix, projmatrix, viewport, wx, wy, sz)
        gluUnProject(x, y, sz,
          mvmatrix, projmatrix, viewport, wx, wy, wz)

        ...
        target.pos.x = wx.value
        target.pos.y = wy.value
29th-Dec-2007 01:00 pm - Bidirectional Communication over AMP
avatar


The following is a simple server that exposes two commands (Echo and Foo). The responder for Foo, in addition to returning an empty dict, will try to call Echo on the client.



ampbidir.py
from twisted.internet import reactor
from twisted.protocols import amp

class Foo(amp.Command):
    response = []

class Echo(amp.Command):
    arguments = [('value', amp.String())]
    response = [('value', amp.String())]


class TheServer(amp.AMP):

    def foo(self):
        def e(v):
            print '[TheServer]', self.transport.getPeer(), 'echoed', v
        self.callRemote(Echo, value='hello').addCallback(e)
        return {}
    Foo.responder(foo)

    def echo(self, value):
        print 'Echo:', value
        return {'value':value}
    Echo.responder(echo)


def main():
    from twisted.internet import reactor
    from twisted.internet.protocol import Factory
    pf = Factory()
    pf.protocol = TheServer
    reactor.listenTCP(1234, pf)
    reactor.run()

if __name__ == '__main__':
    main()



The client will simply start up and call Foo to demonstrate bidirectional support in amp:




client.py
from ampbidir import TheServer, Foo

from twisted.internet.protocol import ClientCreator
from twisted.internet import reactor

client = ClientCreator(reactor, TheServer).connectTCP(
        '127.0.0.1', 1234).addCallback(lambda p: p.callRemote(Foo))
reactor.run()



In the above example, we expose Foo and Echo from the client to the server by creating the client with the protocol TheServer, rather than the generic amp.AMP which exposes nothing, of course.

28th-Dec-2007 06:42 pm - Hah
avatar
from twisted.python import filepath
import mmpython as mmp
import time

EXT = '.mp3'
TEMP = '%2.2d_%s_-_%s_-_%s' + EXT
NESTED = True

def rp(s):
    return s.replace('\x00', '').replace(' ', '_').replace(':','').encode('utf-8')

def job(desc):
    info = mmp.parse(desc.path)
    if not info:
        print 'WARNING: no info found on', desc
        return 'No info found on: %r' % desc
    name = TEMP % (int(info.trackno), rp(info.title), rp(info.artist), rp(info.album))
    print 'Parsed', desc.basename(), 'as', name
    if NESTED:
        fp = filepath.FilePath(rp(info.artist), rp(info.album))
        tgt = tgt_dir
        for c in (rp(info.artist), rp(info.album)):
            tgt = tgt.child(c)
            try:
                tgt.createDirectory()
            except:
                pass
        tgt = tgt.child(name.encode('utf-8'))
    else:
        tgt = tgt_dir.child(name.encode('utf-8'))
    return desc.copyTo(tgt)

def main():
    for desc in [f for f in root.walk() if f.isfile()]:
        job(desc)

if __name__ == '__main__':
    import sys
    root = filepath.FilePath(sys.argv[1])
    try:
        tgt_dir = filepath.FilePath(sys.argv[2])
    except:
        tgt_dir = filepath.FilePath('_ip-recover-%d' % int(time.time() * 1000000))
        tgt_dir.createDirectory()
    main()
13th-Nov-2007 01:44 pm - Mural Complete
avatar

The mural is complete ... After about 9 hours and several bottles of acrylic. So this isn't technical but it will be after I embed micro-controllers in the wall to make the animals' eyes glow. Ok, that might freak my daughter out though. Yes, that's a mountain lion, a bunny and a pig. There is a reason for all these elements.

30th-Jul-2007 08:08 pm - Erlang and binary
avatar

Messing around some with erlang, I've really been impressed with the bit syntax which makes dealing with binary data actually fun. In erlang, binaries can be expressed using the following syntax:



 1> A = <<205>>.
 <<"\315">>


So A is no bound to the binary value 205 - which is by default 1-byte in length. With the following syntax, we can extract the first four bits with out any masking:



 2> <<_:4,B:4>> = A.
 <<"\315">>
 3> B.
 13


We can also deduce the first 4 bits without shifting and/or masking:



  4> <<C:4,_:4>> = A.
  <<"\315">>
  5> C.
  12


So, what's the point? Think about binary protocols (mp3, unicode, ip4, ...). Dealing with this in your run-of-the mill language generally entails the same tedious mechanics - gross amounts of bit-shifting and masking values to arrive at the required values to decode the protocol, which necessitates a cognitive shift from the protocol's specification to the implementation. Using erlang's bit syntax allows me to almost match the protocol verbatim. Take for example, the following function which turns utf-8 encoded binary data (probably read from a file) into a list of code points (or blows up correctly if the data is corrupted):



01. utf8points(Bin) ->
02.    utf8points(binary_to_list(Bin),[]).
03.
04. utf8points([], L) -> lists:reverse(L);
05. utf8points([H|T], L) ->
06.    case <<H>> of
07.        <<2#110:3,  _:5>> -> decode2([H|T], L);
08.        <<2#1110:4, _:4>> -> decode3([H|T], L);
09.        <<2#11110:5,_:3>> -> decode4([H|T], L);
10.        <<0:1,_:7>>       -> utf8points(T, [H|L]);
11.        _                 -> exit({decode_error, {bad_bytes, [H]}})
12.    end.



Ok, so here's a synopsis of the above code fragment:



(Lines 1-2) This function takes the binary (Bin), transforms it to a list of byte values and applies utf8points/2 on it.



(Line 4) We use the list L as an accumulator for the code points. If the list of byte values has been exhausted, we simply return the list reversed - since we are appending to the head of the accumulated list along the way..



(Lines 5-12) The list of bytes has not been exhausted. We consider the first byte of the list to decide whether this bytes alone or in combination with 1-3 bytes following it should be used to determine the code point. If it's one byte, (line 10), we prefix the accumulator and recurse with the remaining bytes. In the other cases we pass control to helper functions (decode2, decode3, decode4), depending on the leading bits of the byte. (2#N is used to represent numbers in base 2).



And decode3 uses some similar logic:



decode3([A,B,C|T], L) ->
    case {<<B>>, <<C>>} of
        {<<2#10:2,_:6>>,<<2#10:2,_:6>>} ->
            <<V:16>> = <<A:4,B:6,C:6>>,
            utf8points(T, [V|L]);
        _ ->
            exit({decode_error, {badbytes, [B,C]}})
    end;
decode3(_, _) ->
    exit(error_eof).


decode2 and decode4 are left as an exercise to the reader ;)

16th-Apr-2007 09:24 pm - Two CPUs are Fun
avatar
cpuinfo from proc:


processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 15
model name : Intel(R) Core(TM)2 CPU T7600 @ 2.33GHz
stepping : 6
cpu MHz : 1000.000
cache size : 4096 KB
physical id : 0
siblings : 2
core id : 0
cpu cores : 2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 10
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge
mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm
constant_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm
bogomips : 4661.07
clflush size : 64

processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 15
model name : Intel(R) Core(TM)2 CPU T7600 @ 2.33GHz
stepping : 6
cpu MHz : 1000.000
cache size : 4096 KB
physical id : 0
siblings : 2
core id : 1
cpu cores : 2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 10
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge
mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm
constant_tsc pni monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr lahf_lm
bogomips : 4655.17
clflush size : 64


Task count per processor:


$ for cpu in 0 1; do echo cpu $cpu task ct : `ps axf -o psr | egrep "$cpu" | wc -l`; done
cpu 0 task ct : 65
cpu 1 task ct : 53



CPUTIME in seconds on each processor (low load):


>>> o = commands.getoutput('ps axf -o psr,bsdtime')
>>> seconds = {'0':0,'1':0}
>>> for line in o.split('\n'):
... tks = line.split()
... if tks[0] == 'PSR': continue
... ms,sec = tks[1].split(':')
... psr = tks[0]
... seconds[psr] = int(ms) * 60 + int(sec)
...
>>> seconds['0']
5
>>> seconds['1']
196
16th-Apr-2007 08:25 am - Ubuntu Dapper/Edgy/Feisty Broadcom Corporation NetXtreme BCM5752 (module bcm43xx) Wireless Nightmare
avatar
Do NOT use firmware (at least from cafeugo) or modules for Broadcom wirless cards - you will experience much pain.

Read this ... please: http://www.linuxquestions.org/questions/showthread.php?t=463002

In short:


  1. Blacklist el' bcm43xx
  2. Install windows driver from here using ndiswrapper
  3. And the ndiswrapper module as an alias for your ethernet device (ex. eth1).



My setup:

djfroofy@kieru:~# uname -a
Linux kieru 2.6.20-15-generic #2 SMP Sat Apr 14 00:54:01 UTC 2007 i686 GNU/Linux

djfroofy@kieru:~# lspci
...
09:00.0 Ethernet controller: Broadcom Corporation NetXtreme BCM5752 Gigabit Ethernet PCI Express (rev 02)
0c:00.0 Network controller: Broadcom Corporation Dell Wireless 1390 WLAN Mini-PCI Card (rev 01)

17th-Nov-2006 11:49 am - vimrc Revisited
avatar
After upgrading to vim 7 I began to notice some subtle (and annoying) effects on my unwieldy
vimrc configuration due to years of impatient hacking and mindless cutting and pasting from
examples. Auto indentation seemed to be erratic for one thing. So I've scrapped my original and started from scratch doing some research on appropriate settings for python-centric hacker like myself. It seems to be working nicely so far, so I'll post here for anyone else interested:

" For Python, replace existing tabs with 8 spaces - we use ``filetype indent on`` so it
" isn't a problem
au BufRead,BufNewFile *.py set tabstop=8
au BufRead,BufNewFile *,*.py set expandtab
" This is good for non-*nix machine - force proper newline '\n'
au BufNewFile *,*.py set fileformat=unix

set encoding=utf-8
filetype indent on
" Haven't noticed any difference in highlighting but here it is
let python_highlight_all=1

" Haskell specific settings
" Don't automatically insert comment header continuation '--' (it's annoying)
au BufRead,BufNewFile *.hs,*.lhs set formatoptions-=c formatoptions-=o formatoptions-=r
" We have a nice plugin for smart python folding, but for haskell ... we'll
" base it on indentation alone:
au BufRead,BufNewFile *.hs,*.lhs set foldmethod=indent

" This could get annoying, you'd be oh so tempted to waste time cleaning them up
" set list listchars=trail:_

syntax on
colorscheme desert

map gs :%s
" Kill the annoying beeps
set noerrorbells
if has('autocmd')
    autocmd GUIEnter * set vb t_vb=
endif
This page was loaded Jul 25th 2008, 7:04 am GMT.