Home / Chess III Arena

CHESS III ARENA

Chess III Arena is a simple chess game that utilizes the Quake III Arena characters. It was written as my first (allegedly) non-trivial ocaml application so I could see what I thought about the language. But more about that later. Most normal people are probably just interested in playing the game. At the moment it is for human players only, there is no computer opponent; it is basically just a virtual chess board. A computer opponent and/or playing remote opponents may or may not appear in future versions.

Notes on Art

Although the engine for Q3A was released as open source, the models are still copywrited by Id Software. To play the game, you will need a copy of the original Quake 3 files. In particular, the file Qyake3/baseq3/pak0.pk3 from the CD contains everything you need. [If you're a modeller and would be interested in providing an alternate set of .md3's that I can distribute under a BSD-style license, please shoot me an email at olsongt at verizon net. It would be nice to have a stand-alone install without dependencies.]

Installation

Windows

  1. Download the executable in zip format.
  2. Extract the files. [Right-click, choose Extract All, Next your way through the wizard.]
  3. Either:
  4. Navigate to the Quake3/baseq3 directory.
  5. Copy the file pak0.pk3 to the c3a directory created when you extracted c3a.
    Note: If you are copying from a local install, you must copy and paste the file. [Right-Click copy, Right-Click paste in the destination directory.] If you drag-and-drop, it will move the file and break your Quake 3 install
  6. Rename the file pak0.pk3 to pak0.zip. [Right-click, select rename]
  7. Extract the contents. [Right-click, choose Extract All. Next your way through the wizard
  8. Verify that the files are in the right place. From the c3a directory, you should be able to navigate to the following directory: pak0/models/players
  9. Double-click on c3a.exe and enjoy!

Linux

Note: Although the program should run on any OpenGL implementation, you're really going to need some sort of hardware acceleration for reasonable rendering performance.

  1. Install OCaml 3.09 or greater and the accompanying LablGL library, however that is done on your distro.
  2. Grab the source .tgz, extract it somewhere.
  3. Within the new c3a directory, create a subdirectory called pak0
  4. Locate the file pak0.pk3 from either your Quake 3 installation or the Quake 3 CD. It is located under the subdirectory Quake3/baseq3 on the CD
  5. Rename the file to pak0.pk3 [mv pak0.pak pak0.zip]
  6. Unzip the contents. [unzip pak0.zip]
  7. Verify that the files are in the right place. From the c3a directory, you should be able to navigate to the following directory: pak0/models/players
  8. Go back to the original c3a directory.
  9. run ./make_native.sh
  10. Right now there is no 'install', you run out of the working directory
  11. Run ./c3a.exe and enjoy!

Playing the Game

When you run the game, you should initially see a title screen. Click anywhere on the screen to start the game. You will see an overhead view of the board. Blue is "white" and Red is "black", so Blue goes first. Click on the piece you want to move. Click on where you want to move it. If you want to change pieces (that is, you haven't 'taken your hand off the piece yet') simply click on a square that is in invalid move location, such as the piece itself. Repeat for red. Repeat until someone checkmates.

Notes on Objective Caml

As I mentioned earlier, this program is my first (allegedly) non-trivial application in OCaml. Since I don't write enough to have genuine blog, I've decided to write about the experience here. Firstly, I'd like to acknowledge that I'm still in the honeymoon period with OCaml; so keep in mind this isn't a particularly objective critique. That being said, I will try to talk about concrete issues and items, instead of discussing the usual "functional programming will expand your mind", "it'll make you a better programmer even if you don't use it", or other arbitrary issues such as code elegance. This also isn't intended to be an exhaustive list; I'm just trying get you interested enough to start googling around. If I can get at least one child to use OCaml, then it was worth it. ;-)

For some reason, I had never gotten around to anything in the ML family even though I've programmed in a variety of languages. Conventional wisdom says that you can either have an expressive programming language (like python or ruby) or a fast programming language (like C, C++, or assembly). But these guys and gals in the ML world have been saying that you can have the best of both worlds. You can have an high-level language that performs like a systems-level language. It sounded too good to be true. So I decided I was going to learn Objective Caml, hoping that it would have 75% the expressiveness of of python, and 75% the performance of C. In the end, I'd say it has about 50-60% the expressiveness of python, and 90% the performance of C. Keep in mind that you can do some really honestly truely wacky stuff in python, so 60% is still really good.

Dynamic vs. Static Redux

I wish I could go back in time and retract some statements that I've made in the inevitable dynamic vs. static flame wars. The standard dynamic arguement is that you can prototype faster because you don't have to deal with the write-compile-run loop. You can write code interactively in a shell (a.k.a. REPL), test it out, and when you're done you can copy it into a source file. Well it turns out OCaml has an interactive shell that works just as well as any dynamic language's shell. For me, that interactive shell is the killer 'dynamic' function, and worth more than eval and the ability to modify and replace objects at runtime and all of the other dynamic stuff. Not that those aren't useful, they're just not critical for me.

Also, realistically, the interactivity really only helps at the beginning of a project when you're roughing out ideas and programming in the small. Ultimately (at least for me) even in a dynamic language you end up making changes, running unit tests, and running an application. At that point you're essentially in a write-compile-run loop, even if you're running 'python start_app.py' instead of hitting F5 in Visual Studio. Although you can put in some hooks modify running systems in a dynamic langague, to be honest live-coding on a production system scares the hell out of me!

Types can actually be useful for us humans!

I always thought of types in newer languages as a hack for lazy compiler writers. That's because most languages used what I'll call (with the completely non-authoritative made-up inaccurate term) 'primitive types'. This is exemplified in Java, where you have int, float, double, and char which lie in some supernatural realm outside of the systems object model. Usually it's done because these can be translated directly to primitives that the CPU can understand and it's more efficient than using some sort of object. Really though, the problems with these types is that you're stuck with whatever the language gives you.

OCaml does types right. For example, if you're dealing with measurements, you can define new types with something like:

  type foot = Foot of int;;
  type meter = Meter of int;;
Then you can create respective values with the constructors:
  Foot 1;;
  Meter 3;;
Now this was really easy to do, so what's the big deal? Well I can never, never, ever accidentally add/subtract/multiply/devide a foot and a meter, resulting in an invalid measurement calculation. This is what ML programmers refer to as 'type-safety'. Also, I was able to do this easily without jumping through a lot of hoops, and it makes the code more readable. I didn't have to go through some static-typing 'straight-jacket' or perform any static-typing 'S&M' like the old me would've said in a dynamic vs. static flame war.

How do you do this in other 'conventional' languages? Sure, you can use 'typedef' in C, but that's really more of a preprocessor directive. I honestly forget what the compiler does if you mix typedefs with the same underlying primitives (i.e. typdef int Apples;typedef int Oranges; Apples A=0;Oranges B=1;A=B;) but I seem to think you'll only get a warning, if that. And Java doesn't even have typedefs, forcing you to write a bunch of boilerplate class code to simulate the feature.

Similarly, you get union types for free in OCaml. In fact, I'm sure OCaml programmers were groaning at the previous example. It should've been:

  type length = Foot of int | Meter of int;;
Now you can still use the same constructors, but they have the same type. Note that even though they're the same type, you still can't add a Foot and a Meter accidentally. But because they're the same type you can put them in a list:
  [Foot 3;Meter 4;Foot 5];;
Once again, doing something like this gets ugly in the C world, you're stuck creating a union type where the first field is an enum. And every single time you use it, you need to check that enum value to make sure you're using it properly! If you forget or decide to cheat just once, you can have (at best) a program with bad results or (at worst) a fatal error. Similarly in Java you would need to come up with some convoluted inheritance hierarchy to try to represent this so you could trick the compiler. Once again leading to accusations of straight-jackets and S&M.

So to summarize, the simplicity of the type system actually makes sense to me as a human. I'm using the type system to make things clearer to me, not to trick the compiler or as a performance optimization.

Generics done Right

Okay, in the OCaml world they use the term 'polymorphism' but you're probably used to hearing 'generics' or 'templates'. Here's a normal function in OCaml that simply takes some value and returns it. Yes, it's a stupid function, but it should be easy to understand:

  let return_it x = x;;
And now here's the polymorphic (a.k.a. generic) version that takes any type:
  let return_it x = x;;
Did you notice the difference between the two functions? If not, look again. There is no difference! The compiler is smart enough to know that that is generic, and will automatically create the right versions when you feed in different types:
  return_it 1;;
  return_it "foo";;
  return_it (Foot 1);;

Okay, now for a more concrete example. Suppose I'm writing a little RPN calculator that takes a list of instructions and calculates the result. I liked the fact that in (say) python I could just write a list like:

  calculate([1,2,4,5,6,"+","+","+","+",5,"/"])
Now in the static guy in my flame war would say, "Yeah, that's nice, but what happens when someone accidentally sends in a list with a file handle in it? The compiler should catch that." To which I would respond, "Well it'll blow up, but (1) it's the dude's own fault for sending in a file object, and (2) writing a static list that takes both numbers and chars is a pain in the ass." Once again because I would have to use a C-style union, or some bizarre inheritance hierarchy plus templates, and write boilerplate code.

But once again, the problem was I wasn't using a type system for humans, and if I could get the compile time safety for free, I'd take it. In OCaml, I could create a union and use that:

  type rpn = Number of int | Plus | Minus | Divide | Multiply;;
  let result = calculate([Number 1 ; Number 2 ; Number 3 ; Number 4 ;
  Plus; Plus ; Plus ; Number 4 ; Divide]);;

I get a generic, but type-safe list. That is I can't append a Foot or Meter to the list. And I get it for free. Why wouldn't you want that?

In Conclusion

I could probably go on and on, but that's enough for now. As I mentioned earlier, I wanted to learn OCaml to see if arguments about Static vs Dynamic, or Performance vs Productivity are really a false dichotomy. And yes they are. Historical accident is responsible for many people's views on these topics than any immutable laws of comp sci. OCaml's slogan should be "Why Compromise?"

Anyway, if I've managed to get you interested, go ahead and download OCaml. If I were you, I'd also start off reading the Jason Hickey's informal introduction, and then move on to the english translation of OReilly France book for a more exhaustive introduction.

olsongt at verizon net 2007-07-01