Monday, December 29, 2008

The Year In Review

Today marks one year since I publicly released the source code for BrainWorks, the artificial intelligence rewrite for Quake 3. I've written slightly more than once per week, giving roughly equal time to the technology behind BrainWorks and general philosophical topics related to AI. While I have more things to write about, I feel like I've covered the basics of BrainWorks in sufficient detail. If people want to hear more about a specific algorithmic part of the code, please let me know and I'll write about it. But for this next year I'm going to focus on more abstract topics likes philosophy, ethics, religion, and the meaning of intelligence. And of course there will be computer science related topics like programming style, structure, game design and so on.

I confess I'm been a bit disdainful of blogs. This is primarily because they are so many of them, and they always focus on the same thing: whatever the author finds interesting. The problem is that so few people have interesting thoughts. And now internet fads like Twitter have made it even easier to share information no one cares about. That's the reason I rarely link to other blogs: It's only when they have something profound to say. If I can only regurgitate someone else's ideas rather than writing new thoughts myself, those ideas had better be very meaningful. The only things worth writing are those things worth reading, and 99% of blogs break this rule.

With that in mind, I plan on doing posts once every two weeks rather than once a week. Quality is far more important than quantity, and good posts can take several days (or weeks) of thinking to best articulate. Here's a rundown of some topics I'd like to cover:
  • Reading between the lines (extracting truth from lies)
  • Living as a non-Christian who still has Christian ethics
  • What does it mean to have feelings?
  • Designing games that invoke emotional responses
  • Intellectual consistency (why I'm not offended when people pray for me)
  • Learning how to learn
If you want to hear my opinions on other topics, now is your chance to make your voice heard.

Monday, December 22, 2008

The Meaning of Truth

I recently had a conversation with my mother where she was bemoaning the current bias in media. She is very conservative, so hearing about the liberal media bias is something I've heard about from her for over a decade. But this time her point was different. For the first time in my memory, she called the conservative media sources untrustworthy as well. And if you can't trust Fox News, how do you find out what's really true? Everyone's message is tampered with their own personal biases.

I explained to her how facts ("truth") can and must be extracted from the biased information we receive from others. Nothing can be accepted at face value, even one's own first impressions. In other words, I believe there's no such thing as unbiased information. In contrast, my mother's contention is that people can and do recognize absolute truth, but immoral people twist it to suit their own purposes. I spent a good amount of time thinking about this dichotomy, and I believe what side you fall on depends on how you answer the following two questions:

1) Can absolute truth be perfectly understood?
2) Can absolute truth be communicated without distorting it?

My mother believes that the answer to both of these questions is yes for the vast majority of truths. My personal belief is that absolute truth cannot be perfectly understood, but you can get a really close approximation most of the time. And it's possible but extremely hard to communicate absolute truth without distortion. That's why learning to extract truth from imperfect information is such a crucial skill for a free thinker.

The problem is that statements must fit within the confines of language to be expressed to others, but not everyone has the same underlying definitions. Much of language depends on context. For example, consider this claim by Alcoholics Anonymous:

Alcoholism is a disease.

The majority of lay people believe this is true, while most medical professionals disagree. Whether you agree with this statement depends on how you define a disease. And that in turn depends on what conclusions you want to apply. The impression I get from Alcoholics Anonymous is that it's important to classify alcoholics as diseased so they can receive sympathy for their troubles in life. If alcoholism were caused by personal choices, then friends and family would be more inclined to chastise the alcoholic than rally around and support them in their addiction.

Meanwhile, medical professionals think about diseases differently. When someone has a disease, that means specific kinds of medication and treatment may be effective. From a medical perspective, diseases tend not to require psychotherapy to treat. So calling alcoholism a disease is misleading for the purposes of treatment. But it's a reasonable statement if you define a disease as, "a malady that requires outside assistance to treat."

The question is which definition of disease are people thinking of when you use the word, and I tend to think the medical professional definition is the more common usage. After all, if all ailments that required psychiatric help were considered diseases, that would include depression and schizophrenia, and no one is claiming that is the case. That's a sign the statement "alcoholism is a disease" lacks internal logical consistency. Nevertheless, that statement is still true within the context of a rather contrived set of definitions.

Perhaps its best to think about statements as belonging to one of two categories: scientific or sociological. A scientific statement is any statement that uses precise terms that are universally understood and agreed on. For example, "the sum of 0 and 1 is 1." Scientific statements, being the basis of science, must be both well defined and measurable.

A sociological statement is anything that's not scientific and therefore not well defined. For example, "Pablo Picasso was a great artist." Not all sociological statements are obviously opinions, however. The statement, "Rappers are musicians" depends on your definition of musician. I personally think of rap stars as poetry artists and not musicians, because their work lacks both melody and harmony.

Even statements that are thought of as facts can be disputed. Consider the following statement:

Senator Obama won a decisive victory over McCain in the past presidential election.

Most but not all people believe this statement is true. The disagreement centers around the definition of decisive. It's true that Obama had over double the electoral votes of McCain, but Obama only had 13% more votes than McCain had. Some people claim that a victory is not decisive when 46% of the country voted for the loser. (I disagree for a variety of reasons, but that's not really the matter at hand.)

The further statements get from scientific statements things like "0 + 1 = 1" and the closer they get to sociological statements like "Rappers are musicians", the harder they are to agree upon and therefore test. And as a result, their absolute truth becomes impossible to verify and bias works its way in. Unfortunately, it is sociological statements that interest most of humanity, not scientific ones. Almost every statement you've read in this post has been a sociological one-- its truth can only be approximated, not scientifically tested and verified. The vast majority of "facts" you interact with on a daily basis are untestable. That is why its so important to analyze whatever you hear rather than accepting it as fact. It's also why artificial intelligence is the most difficult area of computer science. AI is the area that deals with answering sociological questions like "what is best" or "what do you want".

Monday, December 15, 2008

First Causes

In recent years, I've come to realize the most formative event in my life occurred when I was three years old. My parents took us kids out on a walk, and for reasons that aren't clear to me, we didn't take the dog alone. My parents put him out on the deck so he could get fresh air, but kept him on a leash tied to the deck so he wouldn't run off and get lost. When we got back an hour later, I ran ahead to the house and discovered the body of our dog hanging from the deck. He had jumped over the railing to follow us and accidentally hung himself on his leash.

This would have been a traumatic experience for any child. I'm certain that if my parents had found the body first, they would have tidied things up a bit and told me a good lie to lessen the blow of what actually happened. But I found him first, and the memory is firmly etched in my mind. As my three year old mind tried to make sense of the situation, I was confronted by an overwhelming sense of loss and waste. I wasn't just sad that my dog had died. I was particularly hurt by how needless the death had been. If my parents had taken my dog along, left him in the house, or tied him to a low post on the deck staircase, none of this would have happened. I didn't blame my parents for not thinking of this. It was just an unlucky situation that could easily have been avoided.

I didn't recognize it until decades later, but that experience framed the rest of my life. It wasn't framed with an objective or a rationale, but with an emotion:

I despise waste.

I've lived my entire life in a constant struggle against inefficiencies and minimizing potential risks. I work hard to prevent problems before they occur. For example, when I get out of any car and shut the door, I always try the handle to make sure the door is locked. I always check that I bring my wallet with me when I take my keys and vice versa. If I have to be somewhere in an hour and it will take forty minutes to get there (including the margin of error), I find something to do for exactly twenty minutes.

I think this is why I was attracted to computer programming as a profession, and why I'm so good at it. Good programming means you can teach a computer to do menial tasks that would otherwise cost a human lots of time. I'm anal about error checking and commenting in my code because I absolutely do not want things to go wrong. Carefulness has become a way of life for me, so "clean" programming is second nature. Many programmers complain that writing good code takes a lot more time than sloppy code, but I don't agree. I've been writing good code so long that it's actually faster than writing "bad code".

As the story left off last week, I had finished writing the most impressive homing missiles ever. And for game balance reasons, I couldn't use the work the way I wanted to. Obviously this pushed my buttons about wasting time and effort, so I designed a Quake 3 game modification (aka "mod") using the homing missiles. Thus Seeker Quake was born. As this was released over eight years ago, it's a bit hard to find sites where you can download it, but I believe it's still available from this site.

The PlanetQuake article does a good job of summarizing the game, but here's the basic gist. Each player starts with a rocket launcher and gets one seeker shot active at any point in time. When you shoot, it selects a random person with higher score than you and mercilessly tracks them down. They can try to outrun it, but they absolutely cannot hide from the seeker. While your seeker is active, all other rocket shots act like normal rockets. And of course, you can use any other weapon available on the level too.

It's a pretty simple twist but it has a really interesting effect on game balance. I tried games with four to eight players on it of varying skills. On a typical level with a point limit of 20, the scores would probably range anywhere from -2 to 20. But in Seeker Quake, final scores were generally in the 13 to 20 range. The best player still pretty much always won and the worst player always lost, but the range was much tighter. Seekers act as handicapping mechanic, as the best player got targeted with a lot more seekers. He has to work harder to maintain his edge. Meanwhile, the worst players on the level have almost no seekers targeting them, so they have more time to catch up.

Here's the bottom line: When a bunch of players with widely varying skill play Seeker Quake, everyone has a good time and everyone is challenged relative to skill. The game is a total blast.

My life hasn't been all cupcakes and roses. But I've worked hard to turn bad situations into good ones. I'm glad I've taken the time to do so.

Monday, December 8, 2008

Building a Better Wizard

I write a lot about the artificial intelligence in BrainWorks, but that's not everything I program. Seven years ago I created a Quake 3 mod called Art of War. It's a class-based teamplay game, not so different from Team Fortress in that respect. Each class belongs to a faction with its own play style-- there was a faction with strong assault classes and weaker defenders, another with stronger defense and weaker assault classes, and so on. But even the heavily defensive faction needed one competitive assault unit. It just needed abilities that were "in theme" for the flavor of the faction. The resulting class, the Wizard, became a stealth attacker. When upgraded, it could blink through walls and turn invisible, making it ideal for hit-and-run guerilla warfare tactics but terrible for an all-out overrun of the enemy base.

Since each class had three upgrade levels, I was looking for a third ability for the Wizard that was in theme for the stealth assault play style. I decided to try homing missiles. The idea was that the wizard could shoot fireballs that would navigate through corridors and eventually home in on enemy structures. The wizard could assault the enemy base without even being inside it, forcing the defenders to track down the wizard and kill them. That's a very different gameplay situation from defending a swarm of attackers, and variety makes for good gameplay.

Given a choice between doing something the standard way and the excessive way, I usually choose excessive, and it was no different for these homing missiles. First let me explain the standard algorithm for homing projectiles.
  • Repeat every 50 milliseconds:
  • Check for possible targets in the missile's cone of view.
  • If there are no targets, keep heading forward.
  • Otherwise, turn a bit towards the target closest to the missile's forward direction.
That's about it. You can write that in 15 lines of code, and they are reasonably effective. The big problem with these missiles is their lack of memory. They can only latch onto a target that fits in their field of view. If the target can duck around a corner, the missile will forget about them and keep moving forward into a wall. There's no way you could lose track of a real opponent by stepping out of sight for an instant, but the homing missiles just don't know any better.

Not being content with the standard solution, I fortified homing missiles with some industrial strength awesome! These seeker missiles locked onto a target when it was fired and remembered that target's location (even if it went out of view). Obviously the seeker does a standard turn towards the target when it can get line of sight. When the seeker can't view the target, however, it employed the equivalent of sonar to get a navigational reading on its surroundings. The seeker would look in front of itself for walls, pillars, and other obstacles, trying to find large open hallways and rooms. It considered every direction that had sufficiently large space. From that list of possibilities, the seeker picked the direction that was most in line with the target's current position.

Now it's still possible for the seeker to get caught in a corner or alcove. Abstractly this is a local minima: something that appears to be an immediate improvement but is not useful for reaching the optimal goal. To combat this problem, the seeker kept track of how much free space it wanted. Remember, it only considers directions that have enough open space. By modifying thd definition of "enough", the seeker could control the tradeoff between getting to the target and getting to a wide-open area with lots of navigation options.

Whenever the seeker spent time turning, it increased the amount of space it wanted, making it favor open areas (and deprioritize finding the target). And whenever it went straight, it decreased how much space it needed (prioritizing finding the target). The result was that when a missile got stuck in a corner, it would start desparately searching for any hallway it could find, just to get somewhere else. When it found one, it would "relax" a bit and be a bit pickier in the next area. This is a form of simulated annealing.

This entire bit of code took about one week to write and was around 500 lines of code. I tested it for about an hour, tweaked some numbers, and it just worked. Watching it was incredible, as you could see the seekers do amazing things. They would seamlessly turn down narrow hallways with sharp turns, open doors, fly up and down ledges. Think "precise robotics control on a remote control missile" and you'll have the basic idea.

I was really excited to try this out in the next build of Art of War, because I wanted to see how this would affect the play style of the Wizard. To my dismay, it was a total disaster. The homing fireballs ended up being far too good. A wizard could shoot a fireball anywhere on the level and they would automatically find and kill enemy structures. In fact, a team of wizards could launch an assault by standing in their own home base, and there was just no way to defend against that. To balance the class, I had to remove all the cool homing missile code.

Of course, I wasn't willing to let good code go to waste...

Monday, December 1, 2008

A Change of Perspective

This past Thursday was the American holiday of Thanksgiving. President Abraham Lincoln instituted the holiday as an annual occurrence, although the story hearkens to a tale in 1621 of how the native American ("Indians") welcomed the British colonists ("Pilgrims") to American, and how the Pilgrims gave thanks to God for the safety of the Atlantic voyage. Over the centuries, the religious factor was de-emphasized and the holiday was rewritten as a story of the Indians greeting the Pilgrims with a feast, and the Pilgrims thanking the Indians for their hospitality.

It's a fabricated holiday in that the actual feast probably didn't occur, but instead captures the spirit of thankfulness the Pilgrims had. These days, Thanksgiving is a holiday to get together with family and (hopefully) be thankful for the good things in your life. So for most people that means travel, a big meal, and the stress of being with people you might not get along with. But you still have the opportunity for thankfulness if you want to take it.

There's a lot of value in thinking positively. I'm not talking about pretending bad things are good, or completely ignoring things that are obviously issues. Rather I'm referring to appreciating the good things, and not letting problems get in the way of that appreciation. When nine things go well and one thing goes wrong, it's easy to focus on the negative-- it's the part that needs your attention. Everyone sees their current set of problems as the biggest mountain in the world, even if it's really just a molehill in the grand scheme of things.

I've written a lot about the importance accepting that things can't be perfect, even though it's good to strive for it. But that's different from being happy. When you work hard on something and some parts work out while others don't, you can accept this fact with a depressed attitude or with a joyful one. The attitude you pick won't change the world at all, so you might as well pick what makes you happiest. Focusing on the positive things can be hard, but it's a simple thing that makes your entire life better.

So with that in mind, I'm taking this opportunity to remember the successful portions of BrainWorks:
  • Highly realistic aiming
  • Intelligent item pickup selection
  • Context dependent weapon selection
  • Awareness and scanning that lets players try to outsmart bots
  • Dynamic feedback systems that let bots learn as they play
If you've read this blog for a while, you'll know there's a few things I'm not happy with. But to me at least, the project was overall a big success. I met or exceeded the objectives I set and I'm very happy with the results of the AI.

Last, working on this project provided an enormous amount of personal growth which I am also thankful for:
  • Increased self-confidence
  • More self-responsibility
  • Freedom from the burdens of Christianity
  • Less perfectionistic
  • More optimistic
  • Greater joy in life
Working on BrainWorks forced me to take an enormous amount of responsibility. I thought my God would come through for me, but things only came together when I took responsibility for myself. I'm sure the Christian readers will say that this was just God's way of building me up in strength. My perspective is that I finally realized there was no Christian God. But if what really happened was God gave me strength by teaching me not to believe or trust in him anymore, then praise God for that, and I'll continue following his instructions of relying on my own strength.

In short, I feel like I've won at life. This isn't the hardest project I'll ever work on, and not everything in my life is perfect. But working on BrainWorks gave me so much joy and freedom that I know I can handle whatever else life has in store for me. I'm still relatively young (barely into my thirtys) and I figured out the purpose of my life. I love making awesome things, and I plan on doing that as long as I'm alive.