Sunday, July 25, 2010

Three Laws of Robotics

  1. A robot may not injure a human being or, through inaction, allow a human being to come to harm.
  2. A robot must obey any orders given to it by human beings, except where such orders would conflict with the First Law.
  3. A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.

It's clear that droids in Paradroid violate the first two laws, but that doesn't mean that they have to violate the third law as well. After all, Elvex dreamt of world without the first two laws.

So... why would a droid with any brains rush straight into explosion it surely knows will harm it? The most common situation where you see that happening is when a high-speed droid (like 834) shoots a low level droid in front of it and then runs (or flies, 834 is an anti-grav droid) into the explosion, destroying itself. I see no reason for that, so the droid must stop. The fastest way for detecting this was to give every patrol route segment an unique number and update droid info when it leaves a waypoint. When droid is destroyed its type is changed to explosion, but most of other attributes remain. That means that when checking for future collisions I can discard most of the droids/explosions by checking the route number only. That still leaves collisions on or near waypoints, but I have to start somewhere (and I hope I can forget more exact check later ;)

There are 433 waypoint exit directions in total so it was more efficient to write a subroutine to enumerate all routes on fly than including them in the data. That routine is less than 130 bytes long, static data plus depacking code would be at least twice the size. I also needed one 256-byte table to be able to look up the route number fast; 32 waypoints with 8 possible directions form an 8-bit index into that table. I can update route number with "lda waypoint_num; asl; asl; asl; ora dir; tay; lda routes,y; sta droidRoute,x" (that happens only when droid leaves waypoint) and check for impending collisions with "lda droidRoute,x; cmp droidRoute,y" - only if routes match I need to check for the distance between two objects. Nice and fast, now I need to play some games to check if I can see the difference.

Later I can use the same data to check if another droid is in a security droid's way, and if that's the case the security droids may decide to destroy a low-level droid to serve a greater good - to protect the ship.

A robot will guard its own existence with lethal antipersonnel weaponry, because a robot is bloody expensive.
- David Langford

Sunday, July 18, 2010

Not dead yet, part 2

It may have taken two years, but here is the newest and greatest version!

Both original and Metal Edition graphics are now in the same executable, droids have slightly modified AI, high score saving should work with most common expansions (you may have to disable your disk speeder cartridge though) and there is a new frontend. Can you name all the games from where I borrowed something?

Some minor changes including but not limited to

  • orange/red alert increases enemy fire probability half/full ship
  • competition mode - same droids every time, has separate high score file
  • no pacifist bonus if disruptor used
  • droid centered on lift when deck changes - no more exit through wall

File update: two bugfixes to eliminate crash on startup (hopefully one of them does the trick), one minor visual fix and adding disruptor to firing statistics. No more easy accuracy bonus by using 711/742 only!

Friday, January 1, 2010

Crash Boom Bang!

Ever tried disrupting the last droid on ship to smithereens and entering lift when the explosion was still going on? Not? Good. That would have crashed the game right there. Very annoying, especially if you had just quadrupled your all time high score...

What are the changes of someone completing a ship and entering lift before deck is shut down? I don't know, but I managed to do so! Then I reproduced it on purpose to make sure there is a bug. Now I have to hunt it down.

Edit: Found it! It's the same bug which causes "pacifist takeover" (not shooting any droids - except with disruptor!) crash randomly at the same point. In Time is of the Essence I gave you the code for play are top split. In Irq_118() I use this to stabilize raster regardless of how many sprites are over the split:

lda $dc04 ; [1,15] ([2,15] if NTSC/Drean)
eor #$0f ; [14,0] ([13,0])
sta .j3+1
.j3 bpl *+2 ; jump into the delay code
; entering at offset 0 delays 16 cycles,
; entering at offset 14 delays 2 cycles
; OP_CMP_IMM is opcode for CMP #immediate (2 cycles),
; OP_CMP_ZP is opcode for COM $zeropage (3 cycles)
cmp #OP_CMP_ZP

Guess what happens if CIA timer underflows before the interrupt code is executed? It will jump randomly forward and CPU ends in la-la land. Unfortunately there is some code which needs to be run with interrupts disabled, and if top split is delayed because of it... BOOM!

The solution? I can use software semaphore to protect shared resources and avoif disabling interrupts. Nice and clean solution, but why should I do that when I can kludge around the problem by waiting for raster position after the split before disabling IRQ... "bit $d012; bpl *-3; sei" fixes the bug.