Lispings ala John McCarthy

Yesterday, John McCarthy, the inventor of the LISP programming language passed away. It’s been a bad month for computer guys. First Steve Jobs, then Dennis Ritchie, creator of C, now McCarthy.

LISP was one of my early great loves in programming languages. I learned it first in college (prepping for an AI course). Perhaps it was the intensely well written “LISP Bible” Guy Steele’s Common LISP: The Language or Paul Graham’s mind opening On Lisp. Then at MIT my advisors were Patrick Winston (author of LISP) and Rod Brooks. No surprise I took to it. From Paul Graham I learned that programing languages didn’t need to be static, but could become fluid langages shaped to the specific domain or your task. From Rod I learned to roll my own. 

For nearly two decades I was a diehard LISP advocate. I even forced all my programers to code three Crash Bandicoot and four Jak & Daxter games in custom LISP dialects that I wrote the compilers for. This started with our fighting game Way of the Warrior (Rod Brooks voices a character in the game) where I used MacCL to create a LISP syntax state machine to pcode compiler. Then from Crash Bandicoot a much more elaborate language for coding all the gameplay objects called GOOL. In Jak & Daxter I went full on crazy and wrote a native compiler for an object oriented full featured Scheme language called GOAL. We wrote 98% of four Jak & Daxter games in it, including the vector unit assembly.

One of the interesting things about LISP is that it’s actually a pretty easy language to parse, interpret, and compile. This isn’t actually an accident as the S-expression syntax was initially chosen for it’s machine regularity (in those early days of underpowered mainframes). Newer languages are syntactically much more complicated. Ironically most normal programmers, being human, seem to find the more complicated syntax easier and the “simple” S-expression syntax confusing (being backward much of the time to normal human convention). I always found it unambiguous, but go figure. It’s also precisely this regularity that makes the awesome macrology of LISP possible and has allowed the language to remain relevant despite its advanced age.

But by the mid 2000s I started doing the kind of programming I used to do in LISP in Ruby. It’s not that Ruby is a better language (although it is a good one), but mostly it was the momentum factor and the availability of modern libraries for interfacing with the vast array of services out there. Using the crappy unreliable or outdated LISP libraries — if they worked at all — was tedious. Plus the LISP implementations were so outmoded. It was very hard to get other programers (except a couple enthusiasts) to work that way. And ugh, those old CMCL and ACL Garbage Collection code/algo’s were (at least when I last used them in 2006) so awful. In ACL I’d get these LispMachine-like multi-hour GCs.

Ruby had a great book (I put big stock in that) and struck a decent compromise. It’s type system and object model are better (or at least more modern) than Common LISP anyway. The syntax is more inconsistent, and the macro model nowhere near as good. In Ruby you can manually build up strings and feed them into the interpreter, which is equivalent to simple backquote. But you can’t easily do the kind of cool nested constructions that are trivial in LISP.

But it turns out. Libraries and implementation matter a lot. Momentum too. Ruby has momentum, people supporting it who aren’t older than me (and I’m not a young programmer anymore, started in 1980!) Still, you can feel lots and lots of LISP influence in all the new runtime typed languages (Ruby, Python, etc). And 30 years later, listeners still rule! Using a language without a listener is like walking without legs. I pity the C, C++, Java only type programmer.

For more info on my video game career, click here.

For what I’m up to now, click here.

 

So you want to be a video game programmer? – part 4 – School

…CONTINUED from PART 3. Or start at Part 1.

There are two basic approaches: home training and school. Personally I’d recommend both.

Let’s talk school. In my day (1980s) pre-collegiate computer classes barely existed, and if they did they were mostly about Pascal programming and data-structures. They often used p-System pascal, an old-school predecessor to Java!

College Computer Science programs followed (and I imagine they still do) a traditional regimen of stuff like Algorithms, Data-structures, Architecture, Compilers, AI, Theory of Computation etc. They rarely taught or emphasized programming itself. Personally, while I have this training myself (several years at the M.I.T. AI Lab working toward my PhD) I got it long after teaching myself to program and after having 5 published video games on the market.

What I was taught at M.I.T. (1992-94) was way too theoretical to make a good starting place for a young programmer. Don’t get me wrong, I learned a tremendous amount there and it really upped my game. But it was best digested in light of several years practical experience. So I don’t personally think that traditional CS is the way to start. But if you are really serious about computers it is a very solid choice for your higher education. You just need to be ready for it.

And here is the dirty secret about the University Education system: It’s made up of classes. Yep. Your four (or more) year educational experience will just be the summation of eight semesters worth of classes, usually 4-5 per semester. The exact order of these, which topics, and how they are taught will be at the whim of all sorts of varied factors. For example: scheduling, major and general requirements, teacher sabbaticals, friends, personal choice, etc. The school itself will have broad requirements (like you must have 3 science and 2 history classes). You major/department will have more specific ones (like requires 14 classes in the major, with 7 out of the 10 “core” classes — as defined by the department). So everyone’s education is different. That can be a good thing, but it’s less coherent.

And even within a particular class type, like say: Computer Architecture, the classes vary wildly and are rarely designed to work with each other or be taken in a particular order. The school and department might have determined that it should have a Computer Architecture class, but each teacher is free (somewhat) to determine the specific content and style of his or her class. Teachers vary wildly in teaching ability. I mean WILDLY! Even at the best schools. In fact, the teaching quality at M.I.T. was considerably lower than at my undergraduate school, Haverford College. It’s not that the M.I.T. professors weren’t as smart — they were plenty brilliant — but they leaned more toward being famous researchers while Haverford selected people who excelled first and foremost at undergraduate education.

In any case, even within a particular major, say Computer Science, the slate of courses you take might not form a coherent picture. There isn’t much effort made to ensure this. It’s more like, “we need a Compilers course, who wants to teach it?” and then that professor goes off and builds their plan. I’m sure there are constraints and feedback, but it being part of a single coherent program doesn’t seem to be one of them. And teacher style so heavily influences the experience. Now, don’t get me wrong, many of these courses are really good. But they require that you, the student, do a lot of the work integrating the bigger picture. Which really, for first rate minds trying to absorb advanced modalities of thought, is totally fine. It’s just not exactly the same as learning a complex practical field like programming.

But let me speak briefly about the classic topics:

Theory of Computation – Is the cool (but highly esoteric) field of math that endeavors to prove things about what can and not be computed. It includes a lot of discussions about theoretical computers like the Turing Machine and what sorts of computational problems are equivalently complex. This is actually very useful, but only if you have already encountered practical programming tasks. Otherwise it will probably just confuse the bejesus out of you.

Compilers – Is about writing compilers, and how computational semantics are transformed. This is bordering on totally useless for the novice programmer. I myself found it fascinating, but I wrote several compilers. Again, you want to study this several years into your career.

Algorithms – Is the formal study of different methods of problem solving. This is where stuff like the difference between a bubble and an insertion sort goes. Every programmer should know the basic algorithms, but you can read a beginning book fairly early in the learning process and pickup the basics. The college version is much more rigorous. But in the early stages you can lean on libraries which encapsulate these solutions.

Data-structures – These relate closely to Algorithms, but are methods for actually storing data in computers. Different data-structures lend themselves better to different algorithms. The mistake made by a purely academic approach is in thinking that they make a lot of sense without some practical knowledge of the kind of things that you do in normal computing. Still, Algorithms and Data-structures are essential at all levels of programming beyond the totally trivial, and these are the most practical of the classic topics.

AI (Artificial Intelligence) – Can be extremely useful to the game programmer. Games, after all, need enemies that appear intelligent, and in addition have to solve all sorts of big computational problems which use AI techniques (like moving the camera around etc.). But as taught in school it’s pretty theoretical and you need at least a couple years of practical skills first.

Architecture – Is the study of computer hardware, usually micro-processors. A lot of people hate this topic, not being hardware guys. And although you can learn this anytime, you really should. It’s impossible to be a truly great programmer without knowing something about the hardware that makes it all happen. If you are into compilers, this is even more true. I personally loved these classes.

I also want to mention the subject of Programming Languages. Most schools rightfully view the choice of specific programming language as fairly “academic” (or not actually). In the above classes advanced CS guys learn that all normal computer languages are “Turing Complete” and therefore equivalent to each other. Any program in one could be converted to a program in another by automated means (this is what compilers do). Languages all have the same basic features. And if it’s missing one you can write the feature within itself. So who cares which one you use?

This makes a certain academic sense, but in practice, the choice of programming language is vital. And the budding programmer should be introduced to a wide variety of them at a steady yet-not-overwhelming pace so that they learn the fundamentals common to all and do not become one of those lame-ass programmers who are afraid to learn a new programming language. I can be programming in any new language in one day, proficient in a week, expert in a month, master in six. It’s just not that hard.

Schools often have a particular language that they favor. These days it might be Java. In my era it was either Pascal or C. For many schools it’s still probably C/C++. At M.I.T. it was Scheme/Common Lisp! But often professors are also free to just teach a class in a different language. I had an undergraduate AI class all in Prolog. For the gifted student this is a good thing, having a whole class in a new language, as it’s a decent enough emersion to actually learn a new mode of thought (the Prolog class substantially improved my programming even though I’ve not used Prolog since). But also some professors will try the new language for each assignment approach, which is retarded, as there isn’t enough time or depth to master anything, and so the whole assignment becomes about learning the minimum information needed to get it done. The net net is that there is rarely a coherent plan to get you programming and then to have you learn a wide range of practical languages. In said plan, you might start learning with an easier interpreted language like Python, then be taught to master four or five others that are both practical and varied (say C/C++, Java, Javascript, at least one assembly, a “fancy” or two like Ruby/LISP/Scheme/Prolog/Smalltalk etc.) That doesn’t usually happen. You might get lots of Java and a smattering of 10 others.

College professors also don’t usually think that classes that directly and specifically teach programming languages and practical programming are very cool. There is no research or terribly theoretical aspect to them. I.e. the subject isn’t very academic. They are rarely themselves very good programmers (if they were, they’d be off working for Google or whatnot 🙂 but seriously the personality type for “programmer” and “professor” are different — albeit both bookish). This leads to professors rarely adding this kind of class to the curriculum unless someone makes them.

_

Having heard about all these more practical Gaming majors that colleges now have, but which I know nothing about (they didn’t exist 20 years ago), I asked a friend of mine who just finished her CS degree yesterday! Lauren is a fellow blogger, programmer, WOW fan, and budding game designer-programmer. Big congratulations! Her comments are in blue:

Having just completed my degree yesterday, I can confirm that not as much has changed in Computer Science education as one might expect, especially given the exponential growth of the field. Aside from the specific languages taught, which for me was mainly Java instead of Pascal, the curriculum is much the same. The breadth of languages taught is still very much dependent on what you choose to seek out yourself; were it not for honors opportunities or research, I never would have become as familiar as I am with functional programming or the MVC architecture.

After the first two years, programming takes a back seat to theory; upper division classes, while useful and offering a degree of specialization, can be light on actual coding. There are still opportunities to improve your skills, though. Project classes, at least at my school, offer a chance to really show your programming chops, so to speak; with the exception of one I personally considered, all required the completion of extensive coding projects in ten weeks or less to the exclusion of lecture material.

The biggest factor that affected the quality and extent of the education I received was the professors. Sometimes, you will get a truly horrible lecturer, someone who isn’t fair or just doesn’t care. For me, this happened more often than not. The best advice I can give is: Be able to teach yourself. To be honest, I didn’t bother attending classes where the professor was incapable of teaching — I don’t want to waste my time. I went home and read the textbook, or taught myself using tutorials or information online.

“Bad” classes will happen, and the most important thing I learned in college, or even before, is that you need to take active control over your education. Even if the teacher sucks, you can’t blame a failing grade on him; you have the power to learn the material and should do so to the best of your ability. This isn’t to say that poor professor performance doesn’t raise my hackles (it does, a lot), just that self-directed learning is a necessity for succeeding as a student and a programmer, especially since the number of future employers that will accept “The teacher sucked!” as an excuse for a failing grade must be pretty small.

Even if you’re taking the so-called “structured” or “formal” education path, no one will hold your hand. You need to look out for yourself, and find opportunities to broaden your knowledge. I learned firsthand that often these opportunities will not be supplied to you, or even pointed out. You need to be responsible for your own education, especially at large universities. Self-directed study and college are not mutually exclusive.

In that spirit, in addition to my CS degree, I also took a Concentration in Game Culture and Design. This was an interdisciplinary program in conjunction with the art school which, did add a nice game “focus” to my studies. I think these types of programs can be helpful, though to say this improved my coding skills would be more than a stretch. Mostly, it gave me a bit more insight into the game pipeline, and the scale of the work that goes into making a game. I’ve gained some skills which I otherwise wouldn’t have been exposed to; for instance, I’m now comfortable finding my way around game design docs and I’ve had practice giving pitches.

While not a value traditionally espoused as part of a CS education, some gaming or art courses can help your creativity. I can’t speak for the more technical games programs out there, I think there is merit in learning a bit about the industry even prior to leaving school.

This fresher opinion confirmed my belief that no school can be as rigorous as GOOD self training like I gave myself, and under no circumstances should you want until you’re 18 (unless you already are!).

The basic message: Start as early as you can, preferable at age 8-12.

Given that college is roughly age 18-22, and adds a lot of value an education begun at home, it can actually dovetail perfectly with said self education. This will be the topic of a later post in this series.

CONTINUED HERE with The Method!

_

Parts of this series are: [WhyThe SpecsGetting Started, School, Method]

Subscribe to the blog (on the right), or follow me at:

Andy:  or blog

Read more from guest blogger Lauren here.

Or more posts on video gaming here.

And what I’m up to now here.

So you want to be a video game programmer? – part 2 – Specs

…CONTINUED FROM PART 1.

There are a couple of broad categories of programmers working on video game teams. If programmer is your player class, then the following types are your spec. Programmers are all warlocks and mages so instead of “demonology” or “frost” you can choose from below. (NOTE: if you don’t get this joke, you don’t play enough video games) This is the real world however, and many programmers dual (or even triple) spec — i.e. they handle multiple specialties.

1. Gameplay programmer. Programs enemies, characters, interfaces, gameplay setups etc. Probably also does things like AI and collision detection. These programmers are sometimes a little less hardcore technical than some of the other types, but this is the sub-field where the most “art” and experience are often required. Learning how to make a character’s control feel good is not something you can read about in Knuth. It takes the right kind of creative personality and a lot of trial and error. In a lot of ways, this is the heart and soul of game programming, the spec that truly differentiates us from the more engineering programming disciplines.

2. Tools programmer. Works on the extensive tools pipeline that all games have. This is the only branch of game programming where you don’t absolutely have to know and breathe video games inside and out, and it’s a little closer to mainstream applications programming. That being said, life at most video game companies is so intense, you better love them. Tools programmers tend to be very good at practical algorithms, data processing, etc. For some reason, perhaps because it’s more “behind the scenes” this spec is often viewed as less glamourous and there are fewer programmers who want to go into it.

3. Sound programmer. A very specific niche. Here you have to not only know how to program well, but you have to care about the esoteric field of sound. You need the kind of ear that can tell if there is a one sample glitch in some audio loop, and you need to care if the 3D audio spatialization is off or the sound field isn’t balanced. This is often a fairly low level area as audio programming is often done on DSPs.

4. Collision programmer. This is a really specific spec, and often overlaps with Graphics because it involves totally intense amounts of math. You better have taken BC calculus in tenth grade and thought “diffy-q” was the coolest class ever if you want to go into this.

5. Network programmer. In this era of multiplayer and networked gaming there’s a lot of networking going on. And programming across the internet is a bit of a specialty of it’s own. In general, video game programming takes any sub-field of programming to it’s most extreme, pushing the bleeding limits, and networking is no exception. Games often use hairy UDP and peer-to-peer custom protocols where every last bit counts and the slightest packet loss can make for a terrible game experience. If this is your thing, you better know every last nuance of the TCP/IP protocol and be able to read raw packet dumps.

6. Graphics programmer. Some guys really dig graphics and are phenomenal at math. If you don’t shit 4×4 matrices and talk to your mom about shaders, don’t bother. This sub-specialty is often very low-level as graphics programming often involves a lot of optimization. It may involve coming up with a cool new way of environment mapping, some method of packing more vertices through the pipeline, or better smoothing of the quaternions in the character joints (HINT: involves imaginary math — and if you don’t know that that means the square-root of -1 then this sub-field might not be for you).

7. Engine programmer. For some reason, most wannabe video game programmers hold this up as their goal. They want to have created the latest and greatest video game engine with the coolest graphics. Superstars like Tim Sweeney,John Carmack, and even myself are usually seen as falling in this category. The truth is that superstars do all kinds of programming, and are often distinguished by the fact that we are willing and able to handle any sub-type and tie it all together (see lead below). In my mind engine programmers are jacks-of-all-trades, good at building systems and gluing them together. The top guys often blend with Graphics and Lead below. There’s also tons of stuff like compression (nothing uses compression like games, we’d often have 8-10 different custom compressors in a game), multi-threading, load systems (you think seamless loading like in Jak & Daxter is easy?), process management, etc.

8. Lead programmer. People also dream of being the lead. All the great programmers are/were. This is the hardest spec, and no one ever starts out in it. You need to be able to do any of the other specs, or at least judge what approach is best. You need to be able to roll up your sleeves and dive in and fix crap anywhere in the program. You need to live without sleep (4 hours a night every day for years baby!). You need to be able to squint at the screen and guess where the bug is in others people’s code. You need to know how to glue systems together. You need to be able and willing to trim memory footprints and optimize (no one else wants to do it). In fact, you have to know the entire program, even if it is 5-10 million lines of code, and you have to do all the crap that no one else wants to do. Plus, you often have to manage a bevy of other personalities and waste lots and lots of time in meetings. Still want the glory? Being lead is all about responsibility!

CONTINUED with Part 3: Getting Started

_

Parts of this series are: [Why, The Specs, Getting Started, School, Method]

Subscribe to the blog (on the right), or follow me at:

Andy:  or blog

Or more posts on video gaming here.

And what I’m up to now here.

Making Crash Bandicoot – GOOL – part 9

This is part of a now lengthy series of posts on the making of Crash Bandicoot. Click here for the PREVIOUS or for the FIRST POST. I also have a newer post on LISP here.

I’m always being asked for more information on the LISP based languages I designed for the Crash and Jak games. So to that effect, I’m posting here a journal article I wrote on the subject in 1996. This is about GOOL, the LISP language used in Crash 1, Crash 2, and Crash 3. GOOL was my second custom language. Way of the Warrior had a much simpler version of this. Jak 1,2,3 & Jak X used GOAL, which was a totally new vastly superior (and vastly more work to create) implementation that included a full compiler. GOOL (the subject of this article) was mostly interpreted, although by Crash 2 basic expressions were compiled into machine code. But I’ll save details on GOAL for another time.

[ Also I want to thank my reader “Art” for helping cleanup an ancient copy of this article — stuck in some mid 90s Word format that can no longer be read. ]

_

Making the Solution Fit the Problem:

AI and Character Control in Crash Bandicoot

Andrew S. Gavin

Copyright (c) 1996 Andrew Gavin and Naughty Dog, Inc.

All rights reserved.

Abstract

Object control code, which the gaming world euphemistically calls AI, typically runs only a couple of times per frame. For this kind of code, speed of implementation, flexibility, and ease of later modification are the most important requirements. This is because games are all about gameplay, and good gameplay only comes from constant experimentation with and extensive reworking of the code that controls the game’s objects. The constructs and abstractions of standard programming languages are not well suited to object authoring, particularly when it comes to flow of control and state. GOOL (Game Oriented Object LISP) is a compiled language designed specifically for object control code that addresses these limitations.

Video games are the type of program which most consistently pushes the machine and programmer to the limit. The code is required run at blinding speeds, fit in tiny memory footprints, have no serious bugs, and be completed under short schedules. For the ultra high performance 5% of functions which run most frequently there is no substitute for careful hand coding in assembly. However, the rest of the program requires much more rapid implementation. It is this kind of code which is the subject of this article.

Object control code, which is euphemistically called AI in games, typically runs only a couple of times per frame. With this kind of code, speed of implementation, flexibility, and ease of later modification are often more important than maximizing execution time. This is because games are all about gameplay, and achieving good gameplay is about writing and rewriting object code time and time again. Programming languages are not immutable truths handed down from on high, but tools created by people to solve particular tasks. Like any tool, a programming language must be right for the job. One would not attempt to turn a hexagonal nut with a pentagonal wrench, neither is it easy to write a program in a language not well suited to the problem. Sadly, most programmers have only been exposed to a small set of similar and inflexible languages. They have therefore only learned a small number of ways to customize these languages to the task at hand. Let us stop for a second and take look at the abstractions given to us by each of the common choices, and at what price. But first a word about assemblers and compilers in general.

Assemblers and compilers are programs designed to transform one type of data (the source language) into another (the target language). There is nothing particularly mysterious about them, as these transforms are usually just a bunch of tabled relationships. We call one of these programs a compiler when it performs some kind of automatic allocation of CPU resources. Since most commonly found languages are fairly static, the transform rules are usually built into the compiler and can not be changed. However, most compilers offer some kind of macro facility to allow customizations.  In its most general form a macro is merely a named function, which has a rule for when it is activated (i.e. the name of the macro). When it is used, this function is given the old piece of program, and can do whatever normal programming functions it wishes to calculate a new expression, which it returns to be substituted for the old. Unfortunately, most languages do not use this kind of macro, but instead create a new tiny language which defines all the functions which are allowed to run during macro expansion (typically template matching of some sort). With general purpose macros, any transform is possible, and they can even be used to write an entire compiler.

Almost all programmers have had some exposure to assembly language. An assembler basically serves the purpose of converting symbolic names into binary values. For example, “add” becomes 20. In addition, most allow simple renaming of registers, assignment of constants to symbols, evaluation of constant expressions, and some kind of macro language.  Usually these macro languages consist of template substitutions, and the ability to run simple loops at expansion time. Assembly directly describes the instructions interpreted by the processor, and as such is the closest to the chip which a software engineer can get. This makes it very tedious and difficult to port to a different a machine. Additionally, since it consists primarily of moving binary words between registers and memory, and performing simple operations on them, most people find it tedious and difficult to use for large programs. In assembly, all details must be tracked by hand. Since knowledgeable humans are smarter than compilers, albeit much slower, they are capable of doing a substantially better job of creating smaller more efficient code. This is true, despite the claims of the modern OS community, compilers are still only about half as good as a talented assembly programmer. They just save a lot of time.

Many programmers learned to program with Basic. This language has an incredibly simple syntax, and typically comes with a friendly interactive environment. These features make it easy to learn and use. It however has no support for abstractions of any sort, possessing only global variables, and no macro system. Globals are great for beginners because the whole abstract arena of scope becomes a non issue. However, the absence of lexical scoping makes the isolation of code (and its associated bugs) nearly impossible. There is however an important lesson in basic which has been lost on the programming community: interactive is good. Basic typically has an interpreted listener, and this enables one to experiment very quickly with expressions to see how they work, and to debug functions before they are put into production.

The standard programming language of the last few years is C. First and foremost C provides series of convenient macros for flow control, arithmetic operations, memory reference, function calling, and structure access. The compiler writer makes expansions for these that work in the target machine language. C also provides expression parsing and simple register allocation for assembler grade data objects (i.e. words). C code is reasonably portable between machines of a similar generation (i.e. word size). As an afterthought a preprocessor provides rudimentary textual macro expansion and conditional compilation. The choice not to include any of the hallmarks of higher level languages, like memory management (aka garbage collection), run time typing, run time linking, and support for more complex data types (lists, true arrays, trees, hash tables etc.) is a reasonable one for many tasks where efficiency is crucial. However, C is crippled by an inconsistent syntax, a weak text based macro system, and an insurmountable barrier between run time and compile time name spaces. C can only be customized via the #define operator and by functions. Unfortunately, this makes it impossible to do many interesting and easy things, many of C’s fundamental areas, structures, setting, getting, expressions, flow of control, and scope are completely off limits for customization. Since functions always have a new scope, they are not useful creating flow of control constructs, and #define is so weak that it can’t even handle the vagaries of the structure syntax. For those who know C very well it is often a convenient language, since it is good at expressions and basic flow of control. However, whenever complicated data structures are involved the effort needed is obscene, and C in unable to transfer this effort from one data type to another similar one.

Modern operating system and fancy programs are filled with scripting languages.  MS DOS batch language, the various Unix shell languages, perl, tcl etc. are all very common.  These are toy languages. They often have inconsistent and extremely annoying syntaxes, no scoping, and no macros. They were invented basically as macro languages for operating system shells, and as such make it fairly easy to concatenate together new shell commands (a task that is very tedious in assembly or C). However, their ambiguous and inconsistent syntaxes, their slow interpreted execution speeds, and the proliferation of too many alternatives has made them annoying to invest time in learning.  Recently a new abomination has become quite popular, and its name is C++. This monstrosity of a language attempts to extend C in a direction it was never intended, by making structures able to contain functions.  The problem is that the structure syntax is not very flexible, so the language is only customizable in this one direction. Hence one is forced to attempt to build all abstractions around the idea of the structure as class. This leads to odd classes which do not represent data structures, but instead represent abstract ways of doing. One of the nice things about C is that the difference between pointer and object is fairly clear, but in C++ this has become incomprehensibly vague, with all sorts of implicit ways to pass by reference. C++ programs also tend to be many times larger and slower than their C counterparts, compile much slower, and because C++ compilers are written in C, which can not handle flexible data structures well, the slightest change to the source code results in full compiles of the entire source tree. I am convinced that this last problem alone makes the language a severe productivity minus. But I forgot, since C++ must determine nearly everything at compile time you still have to write all the same code over and over again for each new data type.

The advent of the new class metaphor has brought to the fore C and C++’s weakness at memory management. Programmers are forced to create and destroy these new objects in a variety of bizarre fashions. The heap is managed by the wretched malloc model, which uses wasteful memory cookies, creates mysterious crashes on overwrites, and endless fragmentation.

None of these problems are present in Lisp, which is hands down the most flexible language in common use.  Lisp is an old language (having its origins in the 50s) and has grown up over the last 30 years with the evolution of programming. Today’s modern Common Lisp is a far cry from the tiny mainframe list of 30 years ago. Aided by a consistent syntax which is trivial to parse, and the only full power macro system in a commonly used language, Lisp is extremely easy to update, customize, and expand, all without fighting the basic structures of the language. Over the years as lexical scoping, optimized compilation, and object oriented programming each came into vogue Lisp was able to gracefully adopt them without losing its unique character. In Lisp programs are built out of one of the language’s built in data structure, the list.  The basic Lisp expression is the form. Which is either an atom (symbol or number) or a list of other forms. Surrounded by parentheses, a Lisp lists always has its function at the head, for example the C expression 2+2 is written as (+ 2 2). This may seem backwards at first, but with this simple rule much of the ambiguity of the syntax is removed from the language. Since computers have a very hard time with ambiguity, programs that write programs are much easier in Lisp.

Let me illustrate beginning with a simple macro.

(defmacro (1+ value)
	"Simple macro to expand (1+ value) into (+ 1 value).
	Note that backquote is used.  Backquote is a syntax
	sugar which says to return the 'quoted' list, all
	forms following a comma however are evaluated
	before being placed in the list. This allows the
	insertion of fields into a template.
	1+ is the operator which adds 1 to its operand
	(C uses ++ for this)."
  `(+ 1 ,value))

The above form defines a function which takes as its argument the expression beginning with 1+, and returns a new expanded expression (i.e. (1+ 2) > (+ 1 2)). This is a very simple macro because it merely fills in a template. However, if our compiler did not perform constant reduction we could add it to this macro like this:

(defmacro (1+ value)
	"Smarter macro to expand 1+.  If value is a number,
	then increment on the spot and return the new
	number as the expansion."
  (if (numberp value)
      (+ 1 value)
    `(+ 1 ,value)))

The form numberp tests if something is a number. If value is, we do the add in the context of the expansion, returning the new number as the result of the macro phase. If value is not a number (i.e. it is a variable or expression), we return the expanded expression to be incremented at run time.

These full power macros allow the programmer to seamlessly expand the language in new ways. For example, the lisp form cond can be implemented from if’s with a macro. Cond is a special form which is like a C “switch” statement except that each case has an arbitrary expression. For example:

(cond
  ((= temp 2)
   (print 'two))
  ((numberp temp)
   (print 'other number))
  (t
   (print 'other type)))

Will print “two” if temp is 2, “other number” if it is a number (other than 2), and “other type” otherwise. A simple implementation of cond would be as follows:

(defmacro cond (&rest clauses)
	"Implement the standard cond macro out of nested
	'if's and 'when's. t must be used to specify the
	default case, and it must be used last. This macro
	uses backquote's ,@ syntax which splices a list
	into the list below it. Note also the use of progn.
	progn is a form which groups multiple forms and has
	as it's value, the value of the last form. cond
	clauses contain what is called an implicit progn,
	they are grouped together and the value of the
	last one is returned as the value of the cond."
  (if (eq (length clauses) 1)
      (if (eq (caar clauses) t)
          `(progn ,@(cdar clauses))
        `(when ,(caar clauses)
            ,@(cdar clauses)))
    `(if ,(caar clauses)
         (progn ,@(cdar clauses))
       (cond ,@(cdr clauses)))))

This expands the above cond into:

  (if (= temp 2)
      (progn (print 'two))
    (cond
      ((numberp temp)
       (print 'other number))
      (t
       (print 'other type))))

After a single pass of macro expansion. The macro will peel the head off of the cond one clause at a time converting it into nested ifs. There is no way to use C’s #define to create a new flow of control construct like this, yet in a compiled language these compile time transforms are invaluable to bridging the gap between efficient and readable code.

GOOL (Game Oriented Object LISP) is my answer to the difficulties of using C and assembly for object programming. It is a compiled Lisp dialect designed specifically for the programming of interactive game objects. As a language it has the following features: Consistent syntax, full power macros, symbolic names, orthogonal setting/getting, layered computation, multiple ultra light threads, grouping of computations into states, externally introduced flow of control changes (events), small execution size, retargetable backend, and dynamic linking. The GOOL compiler is embedded in Allegro Common Lisp (an ANSI Common Lisp from Franz Inc. which I run on an Silicon Graphics workstation running IRIX). Common Lisp provides an ideal environment for writing compilers because you start off with parsing, garbage collection, lists, trees, hash tables, and macros from the get go. As a language GOOL borrows its syntax and basic forms from Common Lisp.  It has all of Lisp’s basic expression, arithmetic, bookkeeping, and flow of control operators.  These vary in many small ways for reasons of speed or simplicity, but GOOL code can basically be read by the few of us lucky enough to have been exposed to Lisp. GOOL is also equipped with 56 primitives and 420 macros which support its advanced flow of control and game object specific operations. Additional ones can be trivially defined globally or locally within objects, and are indistinguishable from more primitive operations.

The GOOL compiler is an modern optimizing compiler with all sorts of smarts built into various macros and primitives. It is a fully forward referenced single pass compiler. Unlike some other programming languages with single letter names, GOOL does not require you to define something textually before you use it, and you never need tertiary declarations (like prototypes). Computers are good at remembering things, and a compiler is certainly able to remember that you called a function so that it can check the arguments when it gets to the declaration of that function. GOOL is fully relocatable and dynamically linked. So it is not necessary to include code for objects which are not nearby in memory. C is so static, and overlays so difficult and incompatible, that almost no effort is made to do dynamic binding of code, resulting in much wasted memory.

The programming tasks involved in creating game object behaviors are very inconvenient under the standard functional flow of control implied by most programming languages. In the programming of game objects it is necessary for each object to have a local state. This state consists of all sorts of information: type, position, appearance, state, current execution state (program counter), and all types of other state specific to the type of object. From the point of view of a particular object’s code all this can be viewed as an object specific global space and a small stack. This state must be unique to a specific object because it is often necessary to execute the same code on many different copies of the state. In either C or assembly it is typical to make some kind of structure to hold the state, and then write various routines or code fragments that operate on the structure. This can be accomplished either with function syntax macros or structure references. GOOL on the other hand allows this state to be automatically and conveniently bound to variable names for the most straightforward syntax. For example the C:

object >transx = object >transx + immediate_meters(4);

becomes in GOOL the similar expression:

(setf transx (+ transx (meters 4)))

However if in C one wished to add some new named state to each instance of a particular object one would have to create new structure records, accessors, initializers, memory management etc. GOOL on the other hand is able to easily allocate these on the object’s local stack with just one line of code, preserving the data there from frame to frame as well. A standard programming language like C only has one thread of control. While this is appropriate for the general case, it is inappropriate for objects, which are actually better expressed as state machines. In addition, it is extremely useful to be able to layer ultra light weight threads of execution, and to offer externally introduced transfers of control (events). While threads typically complicate most applications programs with few benefits, they are essential to the convenient programming of game objects, which often have to do several things at once. For example, an object might want to rotate 180 degrees, scale up toward 50%, and move toward the player character all at once. These actions do not necessarily take the same amount of time, and it is often useful to dynamically exchange and control them. In traditional code this is very awkward.

The basic unit of code in GOOL is a code block (or thread). These often do simple things as above. An arbitrary number of these may be combined into a state, they may be borrowed from other states, and activated and deactivated on the fly. For example:

(defgstate turn scale and move toward
	:trans	(defgcode (:label turn 180)
		; set the y rotation 10 degrees closer to 180 degrees
			(setf roty (degseek roty (deg 180) (deg 10))))
	:trans	(defgcode (:label scale to 150 percent)
		; set the x,y, and z scales 10% closer to 150% scale
			(with vec scale
				(seekf scale (scale 1.5) (scale .1))))
	:trans	(defgcode (:label move toward target)
		; set the x,y, and z position closer to the target's
		; (another object) position at a rate of 5 meters per second
			(with vec trans
				(seekf trans (target trans) (velocity (meters per sec 5)))))
	:code	(defgcode (:label play animation)
		; play the animation until this object is colliding with
		; another, then change states
			(until (status colliding)
				(play frame group animation))
				(goto collided)))

A :trans block is one which runs continuously (once per frame), and a :code block is one which has a normal program counter, running until suspended by a special primitive (frame), as in “frame is over.” These code blocks can be run as threads (as above), called as procedures, converted to lambda’s and passed to something (function pointers), and assigned to be run under special conditions (events or state exit). In this example is also illustrated the kind of simple symbolic notation used in GOOL to make object programming easier. Vectors like rotation, translation, and scale are bound to simple symbolic names (e.g. roty is the y component of the rotation vector). Many new arithmetic operations have been defined for common operations, for example, seek, which moves a number toward another number by some increment, and seekf its destructive counterpart.

GOOL also has a sophisticated event system.  It is possible to send an event (with parameters) to another object or objects. The object may then choose to do what it wishes with that event, run some code, change state, ignore it, etc., and report something back to the caller. These event handlers can be bound and unbound dynamically, allowing the object to change its behavior to basic events very flexibly.  For example:

:event	(defgcode (:params (event params))
		(reject-event-and-return
			((and (event is hit on the head)
				(< (interrupter transy) transy)))))

Says to ignore the hit on the head event when the interrupter (sender) is below the receiver in the y dimension.

Another feature illustrated here is the indirect addressing mode, (interrupter transy), in which a variable of another object (whose pointer is in the variable interrupter) is accessed. Operations can locate and return object pointers, which can be used as parameters. For example:

(send event hit on the head (find the nearest object))

which sends hit on the head to the nearest object or:

(let ((target (find the nearest object)))
	(when (and target (type target turtle))
		(send event hit on the head)))

which sends hit on the head to the nearest object only if it is a turtle.

It is the GOOL compiler’s responsibility to turn this state into code that executes the abstraction (the above state becomes about 25 words of R3000 assembly code).  GOOL code is typically much smaller than traditional code for similar tasks because the compiler does all the book keeping for this interleaving, and it is all implicit in the runtime product.  In addition it has a degree of code reuse which is practically unachievable in a normal language without extremely illegible source.

GOOL has full power macros which allow the language to be brought up to the level of the problem.  For example, many game programming tasks are more descriptive than a language like C is designed for. The following code causes a paragraph of text to appear on the bottom of the screen and scroll off the top.

(credit list (1 14)
	("AFTER THE")
	("DISAPPEARANCE")
	("OF HIS MENTOR,")
	("DR. NITRUS BRIO")
	("REDISCOVERED HIS")
	("FIRST LOVE:")
	(blank 1)
	("TENDING")
	("BAR"))

It does this by expanding into a program which creates a bunch of scrolling text objects as follows:

(defgopm credit list (params &rest body)
	"This macro iterates through the clauses in its
	body and transforms them into spawn credit line
	statements which create new credit line objects.
	It book keeps the y position down ward by height
	each time."
   (let ((list)
        (y 0)
        (font (first params))
        (height (second params)))
     (dolist (i body)
        (cond
          ((listp i)
           (case
             (car i)
             (blank (incf y (second i)))
             (t (push (append '(spawn credit line)
                               (:y ,y :font ,font :h ,height))
                      list)
                (incf y 1))))))
     `(progn ,@(reverse list))))(defgopm spawn credit line (line &key (y 0) (font 0) (h 18))
  "This macro is purely syntactic sugar, making the above macro somewhat easier."
  (spawn 1 credit line
     (frame num ,line)
     (unit ,(* y h)) ,font))

The following state is the code for the actual credit line.  When one of these credit line objects is spawned it creates a line of text. It then proceeds to use it’s trans to crawl upward from the starting y position until it is off the screen, in which case it kills itself.

(defgstate credit line (stance)
  :handles (spawn credit line)
  :trans (defgcode ()
           (unless first frame
              (setf transy ((parent transy) transvy))
              (when (> transy (unit 140))
              (goto die fast))))
  :code  (defgcode (:params (text frame y font))
           (stomp action screen relative)
           (set frame group text)
           (setf transvy y)
           (setf transy ((parent transy) transvy))
           (sleep text frame)))

As a conglomerate the above code manages to create a scrolling paragraph of arbitrary length from a descriptive block of code.  It does this by using the macro to transform the description into a program to create a cluster of new line objects.  This line objects take their simple behavior and amplify it into a more substantial effect when they are created in concert. In a conventional language it would be typical to create some kind of data structure to describe different actions, and then interpret that. C in particular is a very poor language for description. Because C’s only complex data type, the structure, can not even be declared in line (e.g. “struct foo bar={1,0}” is not legal except as a global) it is extremely awkward to describe complex things. It must be done with code, and the poor textual macro expander is not up to this. Witness the wretchedness of descriptive APIs like that of X windows. The contortions necessary to describe widget creation are unbelievable. Is it no wonder that people prefer to do interface work with resource files or Tcl/Tk which are both more descriptive in nature?

Overall, having a custom language whose primitives and constructs both lend themselves to the general task (object programming), and are customizable to the specific task (a particular object) makes it much easier to write clean descriptive code very quickly. GOOL makes it possible to prototype a new creature or object in as little as 10 minutes. New things can be tried and quickly elaborated or discarded. If the object doesn’t work out it can be pulled from the game in seconds without leaving any hard to find and wasteful traces behind in the source. In addition, since GOOL is a compiled language produced by an advanced register coloring compiler with reductions, flow analysis, and simple continuations it is at least as efficient as C, more so in many cases because of its more specific knowledge of the task at hand.  The use of a custom compiler allows to escape many of the classic problems of C.


A new 10th Crash post can be found HERE.

Subscribe to the blog (on the right), or follow me at:

Andy:  or blog

Also, peek at my novel in progress: The Darkening Dream

or more posts on LISP,

GAMES or BOOKS/MOVIES/TV or WRITING or FOOD.