Tuesday, April 26, 2011

Python C Extension example

Writing a Python extension in C is intimidating. After a week learning the Python/C API, here's a very simple example for a Python C extension, with comments and compilation instructions.
Also on collabedit with highlighting: http://collabedit.com/mj742
//cTest.c

#include

//an example method:
static PyObject* py_double(PyObject* self, PyObject* args){

//self is always here, but only used if it's an
//instance method.
int a;
PyArg_ParseTuple(args, "i:py_double", &a);
//"i:py_double": i for extract one int.
// Py_Arg_ParseTuple(args, "isfo:py_double", &a, &b, &c, &d);
// would extract an (i)nt, (s)tring, (f)loat, Py_(O)bject
// to a, b, c, d respectively.
// After the colon is a string used for exception printouts
// so if this causes an error we'll know it came from here.
return PyInt_FromLong(a * 2);
}

//table of methods to include in this module:
//it's an array of type struct PyMethodDef.
static PyMethodDef test_functions[] = {
/*{python name, c pointer to func, arg format, doc string},*/
{"doubler", py_double, METH_VARARGS, "doubles an int"},
{NULL, NULL, 0, NULL}//sentinal value
};

//This functions is called by Python when you import the module:
//this macro handles void + extern for python module init functions:
PyMODINIT_FUNC
initcTest(void){
Py_InitModule("cTest", test_functions);
//params: module name, method list
}

/*
The name (here cTest) must be consitent in:
1. the filename cTest.pyd (Windows) or cTest.so (Linux)
2. the init method initcTest(void)
3. the first parameter to Py_InitModule("cTest", test_funtions).

note that the name does not have to start with 'c'.

Compiling:
Windows & MSVC:
I use the Visual Studio Command Prompt. These things can also be
configured in the IDE.
1.configure your environment so that the Include path
includes c:\python26\Include (change this for your python location.
Where ever Python.h is). Using the Visual Studio Command Prompt:
> set include=d:\python26\include;%include%
2.compile this to a .dll. Using the Visual Studio Command Prompt:
>cl cTest.c /LD
3.rename it to cTest.pyd
>move cTest.dll cTest.pyd
4.try it in Python!
>python
Python x.y blah blah blah
>>>import cTest
>>>cTest.doubler(3)
6
>>>
Linux & gcc:
1.build an object file:
$gcc -c cTest.c -I/usr/include/python2.6
(change this for your Python include location, where ever Python.h
is.) Note: sometimes Python is installed without the header files.
in Ubuntu you need to get the Python-Dev repository.
2.create a static library:
$ar rcs libcTest.a cTest.o
3.make a shared library:
$gcc -shared -W1,-soname,libcTest.so.1 cTest.o
4.try it in Python!
$python
Python x.y blah blah blah
>>>import cTest
>>>cTest.doubler(3)
6
>>>
*/

note: PSF recommends compiling these using disutils instead. I'm sharing this because of my interest in how the low-level interaction works.

Tuesday, April 12, 2011

pyweek: nanite_fight

I participated in pyweek for the first time last week.  It's a twice-yearly competition to make a game in Python in one week.  I didn't start until Wednesday (looks pretty par for the course), but it was my first time spending 12-18 hours every day on a programming project.  I rather liked it. 

It was also the first time that I aimed to design a relatively small, contained game.  Without resources to develop much content, it makes sense to aim for really emergent gameplay:  a small set of options that can be combined in many different ways to create a large number of outcomes (e.g. the game Go).  

Design
nanite_fight is a programming game.  You get conditions and actions, a limited coding space, and function calls to jump around the code.  You can change the code while it runs.  If your nanite shoots another nanite it makes it into a copy of itself that independently runs through the same code.  A second player can do the same thing.

That's pretty much it, but it does allow for a huge number of permutations.  Unfortunately I didn't have time to make a great interface for two players playing at the same time (but you can load saved scripts from two players).  Also, the skill-curve seems diminishing: a nanite that just turns and shoots is way ahead of a nanite that just shoots.  You can make a nanite that will consistently defeat it, but it will take substantially more time.  Defeating that nanite will a lot of work.  There are too few opportunities to learn a new trick and gain an advantage.

Programming
I set a goal to write this outside of my usual object-oriented paradigm.  I didn't define a single class, and a nanite is essentially a struct of ints in the form of an array.  

All the nanite programming is handled with first-order functions.  The code grids are 2D arrays of pointers to functions, and the nanites store the row and column they're on, do it, then move to the next. 

I made extensive use of hash tables in handling both input (which is all drag-and-drop) and visual and audio output.  The code grid is an array of functions, so the view has a table with function keys and icon values.  Each time a nanite takes an action, another table looks it up in a table with function keys and sound effect values.