The Ur-Quan Masters Home Page Welcome, Guest. Please login or register.
Did you miss your activation email?
December 12, 2024, 07:00:23 pm
Home Help Search Login Register
News: Celebrating 30 years of Star Control 2 - The Ur-Quan Masters

+  The Ur-Quan Masters Discussion Forum
|-+  The Ur-Quan Masters Re-Release
| |-+  Starbase Café (Moderator: Death 999)
| | |-+  My first AI
« previous next »
Pages: [1] Print
Author Topic: My first AI  (Read 1921 times)
jaychant
*Smell* controller
****
Offline Offline

Gender: Male
Posts: 432


Please visit my homepage


View Profile WWW
My first AI
« on: January 22, 2009, 08:03:30 pm »

It's been a few days now since I finished my first (non-random) AI. It's very simple; it tells Mario how to kill several goombas coming out of four doors indefinitely.

The unfortunate thing is, I haven't seen a whole lot of constructive criticism on my AI.

Here is a download (includes the source code, the MarioAI.gmk)
Here is the game on YoYo Games (no download necessary)

What I want you to do is observe the AI (and perhaps peek at the code for the AI below) and give me constructive criticism.

On a side note, the game is actually kind of fun and somehow addicting. I don't know why. (Perhaps because it's an arcade game?)

Here is the code for the AI (Note: GML is very similar to C++.):
(Note: This happens every frame, or in GM terms, at every Begin Step)
Code:
/*
This code is the AI handler. It simulates button presses in order
to create simplicity. The AI presses a key depending on the situation.
This prevents pointless checks already checked for in the Step event.
The AI is turned off when the player chooses to do so (allowing him to
assume control), which is controlled by the variable ai_active
*/

if (ai_active = false) { exit; }

number_below = 0;

//Declaring variable nearest_goomba. Gets deleted after the code execution.
var nearest_goomba;

//Sets variable nearest_goomba to the ID of the nearest goomba object
if (instance_exists(obj_goomba))
{nearest_goomba = instance_nearest(x,y,obj_goomba);}

//Set how many goombas are below Mario; Used later
with (obj_goomba)
    {
    if (below = true)
        {
        other.number_below += 1;
        }
    }

//Danger zone; If goombas are too close, but just on one side, Mario will flee.
//If Mario is surrounded, he will jump instead.
if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true))
//Collision_rectangle checks for collisions within a rectangle.
    {
    if not (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) and not (collision_point(x-20,y,obj_block,false,true))
        {
        //keyboard_key simulates the pressing or releasing of a key on the keyboard
        keyboard_key_press(vk_left);
        keyboard_key_release(vk_right);
        }
    else
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }
else if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true))
    {
    if not (collision_point(x+20,y,obj_block,false,true))
        {
        keyboard_key_press(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }
   
//If not in danger zone, move towards nearest goomba (if a goomba
//exists)
else if (instance_exists(obj_goomba))
    {
    if (nearest_goomba.x > x)
        {
        keyboard_key_press(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        keyboard_key_press(vk_left);
        keyboard_key_release(vk_right);
        }
   
    //Check to see if Mario is in range to jump on the goomba. If he is, jump.
    if (collision_line(x+20,y+8,x+72,y+8,obj_goomba,false,true))
    or (collision_line(x-20,y+8,x-72,y+8,obj_goomba,false,true))
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }
 
//if the previous statement returns false, there are no goombas. Return to the
//center of the screen. (If within a range, stop)
else
    {
    if (x >= (room_width/2) - 10) and (x <= (room_width/2) + 10)
        {
        keyboard_key_release(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        if (x > room_width / 2)
            {
            keyboard_key_press(vk_left);
            keyboard_key_release(vk_right);
            }
        else
            {
            keyboard_key_press(vk_right);
            keyboard_key_release(vk_left);
            }
        }
    }
   
//If too close to the door, move away if safe.
if (x > instance_nearest(x,y,obj_door).x-12)
and (x < instance_nearest(x,y,obj_door).x+30)
    {
    if (x > instance_nearest(x,y,obj_door).x)
        {
        //check that there isn't a goomba close by to the right.
        //If there is, jump and continue left. Otherwise, change movement
        //to right.
        if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true))
        or (x > 515)
            {
            keyboard_key_press(vk_up);
            keyboard_key_release(vk_up);
            }
        else
            {
            keyboard_key_press(vk_right);
            keyboard_key_release(vk_left);
            }
        }
    else
        {
        //check that there isn't a goomba close by to the left.
        //If there is, jump and continue right. Otherwise, change movement
        //to left.
        if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true))
        or (x < 140)
            {
            keyboard_key_press(vk_up);
            keyboard_key_release(vk_up);
            }
        else
            {
            keyboard_key_press(vk_left);
            keyboard_key_release(vk_right);
            }
        }
    }
Logged

Please visit my homepage.
RTyp06
*Smell* controller
****
Offline Offline

Posts: 491



View Profile
Re: My first AI
« Reply #1 on: January 23, 2009, 12:58:43 am »

Let's talk about your code...
Quote
Code:
if (ai_active = false) { exit; }
I personally would not do it this way. Instead I would write it like this..

Code:
if(ai_active == true)
     {
     // All your AI code here.
     }
// No need for an exit statement

This will keep all your code aligned so that it is easier to see when a nested if statement starts and stops. It is also easier to understand. Also, a single "=" (equal sign) is usually used as an assignment operator. Whenever you want to compare two or more values, use the "==" double equal sign.
Quote
Code:
number_below = 0;

Always keep your local variable, default value assignments together and (usually) at the top of your code. In GML, use the var keyword in this case because you are using a script specific variable that is only used inside this code structure. This way no outside script or object can access this local variable.

So I'd write it like this:

Code:
var number_below = 0;

And put it as the first line of code.
Quote
Code:
//Sets variable nearest_goomba to the ID of the nearest goomba object
if (instance_exists(obj_goomba))
{nearest_goomba = instance_nearest(x,y,obj_goomba);}

Never put brackets on the same line as the code. This makes it look cluttered and difficult to read. You didn't do it with the rest of your code, I don't know why you did it here.
Quote
Code:
//Set how many goombas are below Mario; Used later
with (obj_goomba)
    {
    if (below = true)
        {
        other.number_below += 1;
        }
    }

This does not seem efficient to me. You are checking all goomba object's below variable and generating a count upon this variable. And I assume that each goomba object then must decide if it is above or below at each step. Instead I would perform that check right here in this code structure. Somthing like this:

Code:
with(object_goomba)
     {
     if y > object_mario.y
          {
         number_under +=1;
          }
     }

Oh and once again use the == when comparing values not =.
Quote
Code:
//Danger zone; If goombas are too close, but just on one side, Mario will flee.
//If Mario is surrounded, he will jump instead.
if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true))
//Collision_rectangle checks for collisions within a rectangle.
    {
    if not (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true)) and not (collision_point(x-20,y,obj_block,false,true))
        {
        //keyboard_key simulates the pressing or releasing of a key on the keyboard
        keyboard_key_press(vk_left);
        keyboard_key_release(vk_right);
        }
    else
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }
else if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true))
    {
    if not (collision_point(x+20,y,obj_block,false,true))
        {
        keyboard_key_press(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }

Wow.. Ok, let's see...From the Game Maker Help/Manual:

collision_rectangle(x1,y1,x2,y2,obj,prec,notme) This function tests whether there is a collision between the (filled) rectangle with the indicated opposite corners and entities of object obj. For example, you can use this to test whether an area is free of obstacles.

I may have to get back to you on this one as far as using this function to check for nearby goombas. As for the keyboard functions,  I personally would not simulate key strokes. I'd directly make mario jump or run as required. it may be more confusing to the outside eye but, if well commented, this should not be a problem.
Quote
Code:
//If not in danger zone, move towards nearest goomba (if a goomba
//exists)
else if (instance_exists(obj_goomba))
    {
    if (nearest_goomba.x > x)
        {
        keyboard_key_press(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        keyboard_key_press(vk_left);
        keyboard_key_release(vk_right);
        }

Ok now you have already checked to see if a goomba existed earlier in the code so there should not be a need to check if the instance exists again. In fact you could incorporate that check into the very first if statement...Somthing like this:

Code:
if(ai_active == true )&& (instance_exists(obj_goomba) == true)
     {
     // All your AI code here.
     }
Quote
Code:
    //Check to see if Mario is in range to jump on the goomba. If he is, jump.
    if (collision_line(x+20,y+8,x+72,y+8,obj_goomba,false,true))
    or (collision_line(x-20,y+8,x-72,y+8,obj_goomba,false,true))
        {
        keyboard_key_press(vk_up);
        keyboard_key_release(vk_up);
        }
    }

Once again, negative on the simulated keystrokes. This is bad design imo.
 
Quote
Code:
//if the previous statement returns false, there are no goombas. Return to the
//center of the screen. (If within a range, stop)
else
    {
    if (x >= (room_width/2) - 10) and (x <= (room_width/2) + 10)
        {
        keyboard_key_release(vk_right);
        keyboard_key_release(vk_left);
        }
    else
        {
        if (x > room_width / 2)
            {
            keyboard_key_press(vk_left);
            keyboard_key_release(vk_right);
            }
        else
            {
            keyboard_key_press(vk_right);
            keyboard_key_release(vk_left);
            }
        }
    }

Ok I see why you did the second instance check now. Instead, I'd Simply nest the first instance check and then perform all goomba free actions in the else event to make your code more concise and easy to follow. I'm certain you do not have to check twice to see if any goomba obects exist.

Quote
Code:
//If too close to the door, move away if safe.
if (x > instance_nearest(x,y,obj_door).x-12)
and (x < instance_nearest(x,y,obj_door).x+30)
    {
    if (x > instance_nearest(x,y,obj_door).x)
        {
        //check that there isn't a goomba close by to the right.
        //If there is, jump and continue left. Otherwise, change movement
        //to right.
        if (collision_rectangle(x,y+8,x+20,y+20,obj_goomba,false,true))
        or (x > 515)
            {
            keyboard_key_press(vk_up);
            keyboard_key_release(vk_up);
            }
        else
            {
            keyboard_key_press(vk_right);
            keyboard_key_release(vk_left);
            }
        }
    else
        {
        //check that there isn't a goomba close by to the left.
        //If there is, jump and continue right. Otherwise, change movement
        //to left.
        if (collision_rectangle(x,y+8,x-20,y+20,obj_goomba,false,true))
        or (x < 140)
            {
            keyboard_key_press(vk_up);
            keyboard_key_release(vk_up);
            }
        else
            {
            keyboard_key_press(vk_left);
            keyboard_key_release(vk_right);
            }
        }
    }

I'll get back to you on this one... I may try and come up with a more efficient version.. sometime...later. btw I've been a hobbyist programmer for years but am in no way an expert and welcome critisisim from anyone more knowledgable than I.
« Last Edit: January 23, 2009, 01:03:39 am by RTyp06 » Logged
jaychant
*Smell* controller
****
Offline Offline

Gender: Male
Posts: 432


Please visit my homepage


View Profile WWW
Re: My first AI
« Reply #2 on: January 23, 2009, 01:15:31 am »

While I appreciate your criticisms on the bad organization I had, I was looking for criticisms on the behavior of the AI and the methods I used. I made a lot of simplications because this wasn't a serious project, just to practice.
« Last Edit: January 23, 2009, 01:21:29 am by jaychant » Logged

Please visit my homepage.
Arne
Enlightened
*****
Offline Offline

Gender: Male
Posts: 520


Yak!


View Profile WWW
Re: My first AI
« Reply #3 on: January 23, 2009, 06:15:35 pm »

I couldn't figure out how to play it in the browser, but I'm blocking quite a bit of stuff.

I prefer to not make the jump to the AI method at all if the AI is off. I.e. I do the check before I jump. If AI is off I have no business there in the first place.

I'm not familiar with GM code, but I like the idea of a virtual joypad (or keyboard). In my current project, I use OOP, and all my game characters are related to a common ancestor who is capable of using an AI method to manipulate a virtual joypad. I can do fun stuff like making all game characters playable this way, and it makes the code more readable and consitent, I think.
Logged
Pages: [1] Print 
« previous next »
Jump to:  


Login with username, password and session length

Powered by MySQL Powered by PHP Powered by SMF 1.1.21 | SMF © 2015, Simple Machines Valid XHTML 1.0! Valid CSS!