Jump to content
  • entries
    334
  • comments
    900
  • views
    258,315

Leprechaun AI


EricBall

549 views

the enemies are so darn dumb!

 

He's right, I just didn't notice. Well, now I have and I'm going to do something about it. Unfortunately, it's tricker than it sounds.

 

Fundamentally, the enemies are at a major disadvantage to the player. The player can see the entire level; the enemies are almost blind. Not only is it not feasible to put in some kind of path finding algorithm, but I'd like to avoid doing any level data lookups at all. Fortunately, the enemies have a much simpler purpose - chase down the player and touch 'em.

 

Earlier this week I was modifying the enemy's joystick routine to handle left/center/right better so the player doesn't have to be precisely above or below the enemy to register "center". I was hoping this would allow the enemies to fall off ropes when the player passed below them. I also re-enabled some code which changes the enemy color depending on whether they are in hunt or chase mode. After playing a short while I saw the problem. (But not the solution....)

 

In the current version the enemy AI is based around two simple states: hunt and chase. In chase mode the enemy points it's virtual joystick towards the player, while in hunt mode the enemy holds the joystick in one direction, changing that direction when it stops moving. The enemies go from hunt to chase mode when they are going in the correct direction, and from chase to hunt mode when they stop moving. (Falling and dying also kick in chase mode automatically.)

 

It's the chase to hunt transition which causes the main problem. Basically, when the player runs over or under the enemy on a different platform the enemy's joystick points either straight up or down. Unless the enemy is on a ladder this will probably cause them to stop, which then flips them to hunt mode and they stop chasing - i.e. act dumb.

 

Unfortunately, I haven't come up with an ideal solution yet. Right now, the basic structure of the code is:

1. Each enemy has a state byte. It also knows it's position, the player's position, and what it's last action was (i.e. moving, climbing, stopped).

2. Using this information, and nothing else, it needs to determine it's joystick direction (which gets fed into a subroutine, also used by the player, that determines the current action).

3. If desired, the current action could be used as additional information for the state bye.

 

It should also be noted the joystick -> action subroutine has some intelligence regarding diagonal moves. i.e. on ladders up/down is preferred to right/left, and the opposite is true for ropes.

 

Now, I'm thinking that the AI will be more effective (less dumb) if it continued in the direction it was going in rather than always trying to go in the direction of the player. (Although there may be some exceptions for ladders & ropes, which would make "mind control" possible.)

 

However, I'm open to suggestions. What I've found is it's the transitions which are tricky to handle. Not only hunt versus chase, but also simply when to change direction.

8 Comments


Recommended Comments

Glad you're looking at this. :party:

 

One very simple solution would be to not allow the virtual joysticks to point up/down unless the enemy was on a ladder or rope.

 

EDIT: Just re-read this:

Each enemy has a state byte. It also knows it's position, the player's position, and what it's last action was (i.e. moving, climbing, stopped).

2. Using this information, and nothing else

So the enemies don't know if they are on a ladder or not? Hmmm...tricky.

 

EDIT II: Maybe you could make a transition from hunt to chase require two consecutive "stops" - i.e., if a player runs underneath an enemy the enemy tries to go down and so it stops - but since his last action was running, he doesn't change states. Then the next time the enemy tries to move, hopefully the player is no longer directly underneath and so the enemy moves L/R towards him, still in chase mode. But if the enemy still can't move, (i.e., he is stopped and his last action was "stopped") then he switches to hunt mode.

 

Would this work? It depends on how often you move the enemies compared to how fast the player moves. And BTW, I think the "bug" where a player could force a change from chase to hunt by going underneath an enemy and then stopping is a feature - it makes some logical sense (if the enemy can't see the player, since he is directly underneath, and he can't hear the player, since he isn't moving, then logically he doesn't know where the player is), it is subtle enough that you'd have to be pretty familiar with the game to be able to recognize and use it, and it has a cost for the player: to control enemy movements like that he has to come to a complete halt, if only briefly. Anyway, my 2 cents. :party:

Link to comment

I had an idea on the way home from work. Basically leverage the last action as state info. Thus:

falling/dead - point joystick to player

stopped - go into a hunt mode, rotating the joystick around

running - continue in same direction (right/left) with up/down pointing to player

climbing - continue in same direction (up/down) with right/left pointing to player (maybe with some smarts to grab the player if they are next to the ladder

swinging - continue in same direction (right/left) with up/down pointing to player, maybe with some code to fall down if the player is directly beneath

 

Remember, if the enemy (or player) is on a ladder the joystick->action subroutine will give up/down priority, which will start the climb automatically.

 

About the only place this doesn't work is if the ladder goes through or past a platform, since that will mean the enemy will keep climbing even if the player is on the same row.

Link to comment

Surely the movement routine has to know what directions the enemy can move at any given time. Is there any reason that information can't be used by the AI?

 

Also, since you're giving the monsters different colors, would there be any problem giving them slightly-different algorithms?

 

If you can determine in your AI routine the directions in which a monster can move, I would suggest something like the following:

 

-1- If moving toward player, whether vertically or horizontally, keep on doing so (in the same direction).

 

-2- If moving away from player, move toward player in other axis if possible, else keep on in current direction.

 

-3- If unable to keep moving in current direction or move toward player in other axis, reverse direction.

 

-4- If that doesn't work, but can move (away from player) in other axis, do that.

 

-5- Otherwise just sit; move toward player when possible.

 

If you can give monsters different personalities, a few useful traits to consider:

 

-1- One of the monsters might have a very strong "horizontal" attraction to the player, especially when vertical separation is small. So if you take the tightrope immediately over this monster, it will try to stay underneath you.

 

-2- One or more of the monsters may have an aversion to freefall. Perhaps have a 'bounce' flag which indicates how many times the monster has found its movement blocked since the last time it fell; once the counter reaches 2 or 3, the monster could lose its aversion until the next free-fall.

 

-3- If you keep track of what the monster's last horizontal direction was, some monsters could be set to start out in their previous direction when landing after freefall, some could start out the opposite, or some could start toward the player.

 

-4- When moving vertically, whether to regard a 'wrong-way' horizontal move as being preferable to a continued vertical move and vice versa. As with free-fall, a 2-bit counter may be useful here.

 

-5- If you can keep track of the X coordinate of the last latter a monster climbed, if may be possible to increase greatly the apparent sophistication of the monster's movement by having it avoid climbing down a ladder it had just climbed up unless it determined nothing else was apt to work.

Link to comment

Surely the movement routine has to know what directions the enemy can move at any given time. Is there any reason that information can't be used by the AI?

 

Yes/no. The DO_GRID routine does the minimum number of tests necessary to detemine the player/enemy action based on current position & joystick direction. This is because figuring out the type of grid square is computationally intensive because it's based completely on the playfield bitmap. (Although I may add in some player/enemy tests to allow them to walk on each other's heads.) So I'd like to avoid that if at all possible.

 

Also, since you're giving the monsters different colors, would there be any problem giving them slightly-different algorithms?

 

I'd considered the idea. Let's see if I can come up with one decent algorithm first. The one problem with your suggestion is it will probably lead to too much "mind control", i.e. enemies going back & forth (or up & down) in response to the player rather than discovering some way to get closer.

Link to comment
I'd considered the idea. Let's see if I can come up with one decent algorithm first. The one problem with your suggestion is it will probably lead to too much "mind control", i.e. enemies going back & forth (or up & down) in response to the player rather than discovering some way to get closer.

 

That would depend on the arrangement of a level, obviously, but I would expect the monsters to be behave pretty well; their biggest weakness would be with falling off ladders if the code didn't know whether they should walk off ladders without a platform to stand on.

 

Perhaps things could be improved by making it so that a monster climbing a ladder would always prefer to keep climbing in the current direction unless (1) he is at the same level of the player, in which case he should either move horizontally toward the player or climb upward, or (2) he is above the level of the player and moving upward. Some layouts would still tend to have monsters get stuck in silly loops, but that would seem somewhat inevitable in any case, especially if monsters can't sense whether they can walk without falling.

 

In what order is your bitmap data stored? I would think it should be possible to do bitmap lookups pretty quickly.

Link to comment

In what order is your bitmap data stored? I would think it should be possible to do bitmap lookups pretty quickly.

It's a raw PF bitmap. So the code has to AND out the right bit of 8 bytes, which requires over 200 cycles. However, I'm seriously considering adding some kind of "look-ahead" at least for the ladder case, i.e. if the player is in the same row, can the enemy go that direction without falling?

Link to comment
It's a raw PF bitmap. So the code has to AND out the right bit of 8 bytes, which requires over 200 cycles.

 

Why 200 cycles?

GetPixel: ; Gets pixel at address Y,X
 lda bitptr_l,x
 sta ptrl
 lda bitptr_h,x
 sta ptr1+1
 lda (ptr1),y
 and bitmask,x
 rts

4+3+4+3+5+4 = 23 not counting the RTS. And if you need the pixel above or below, just use

 dey
 lda (ptr1),y
 and bitmask,x

or something similar for the new pixel.

Link to comment

It's not just the one byte, I need to retrieve the same bit from 8 bytes.

 

GRID	LDY	YPOS_TMP; check current position	
GRID_Y	LDA	XPOS_TMP	
LSR
LSR		; implicit CLC
TAX
GRID_X	LDA	XBANK,X	; get bitmap bank
STA	PF_PTR+1; set up pointer
LDA	#$00	
STA	PF_PTR
LDA	XFBIT,X	; bitmap mask
TAX		; save mask
GRID_Q	LDA	#$01	; initial value (8 shifts)
STA	PFOUT
1$	TXA		; load mask
AND	(PF_PTR),Y
BEQ	2$
SEC		; shift in 1
2$	ROL	PFOUT
INY
BCC	1$	; keep looping until 1 shifted out
LDA	PFOUT
RTS

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...