Use these links to understand the code I'm posting below since although it looks like PHP it is not:Function ReferenceLanguage ReferenceI have been making a programming language for some time now as a good learning experience etc and improving my programming skills.I'm making a full clone of the original Pengo.  I'm currently doing the AI there is a few Ghosts.Here is a video of the game I'm making Pengo Demo Video.Source code to the game: Pengo Source CodeHere is the website for the game and it's download Pengo wiki which will be helpful in seeing how the AI works etc in order to help me improve it. Also i wouldn't mind improving any aspect let me know your opinions.Yellow ghost will just cruise around the map going clockwise when it hits a wall however it will randomly decide to move in another direction.Blue ghost is similar to Red but if it seeds the player it will begin moving towards the player.Green ghost is similar to Blue but if it gets within 3 blocks of the player it will go frenzy and chase player non-stop.Pink ghost uses A* pathfinder to immediately chase the player and hunt them down no matter where they are.Red ghost is one I'm having problems with I need it to avoid the player at all costs even move out of the way of pushed obstacles.This post will focus on the AI for the Ghosts improvements and the best way to do it also I want to add an avoid AI for the Red ghost so it will avoid players and even dodge blocks pushed at it not sure how making that work perfect.Forgive the lengthy post; I just wanted to make sure you had all the AI that is needed availableI don't really see a way to get the AI posted since if I leave out any parts it might cause more confusionThis is the constructor for the Green ghost AI this is how it creates it's AI and sets it up:// AI$AI = new AI($this); // Creates a CORE AI class$AI->$Enabled = false;my $AIWanderAStar = new AIWanderAStar($this); // Uses a AIWanderAStar$AIWanderAStar->$Enabled = true;        my $AIChase = new AIChase($this); // Uses an AIChase$AIChase->$Enabled = true;$AIChase->$TryChasePlayer = true;$AIChase->$TryChasePlayerDist = 3;$AIChase->$TryChasePlayerLock = true;       my $AIMoveDelay = new AIMoveDelay($this); // Uses an AIMoveDelay$AIMoveDelay->$MoveFor = 200;$AIMoveDelay->$StopFor = 10;$AIMoveDelay->$Enabled = true;$AI->$AIList[] = $AIMoveDelay;$AI->$AIList[] = $AIChase;$AI->$AIList[] = $AIWanderAStar;AI.spkThis is the BASE AI class that all AI's inherit:Class AI{    Static $AIEnabled = true;    my $Entity;    my $Enabled;    my $AIList;    Function __Construct(Entity $E)    {        $Entity = $E;        $Enabled = false;        $AIList = array();    }    Function __Destruct()    {        Dispose();    }    Function Dispose()    {        if($Disposed)            return;        $Disposed = true;        $Enabled = false;        $Entity = null;    }    Function Update()    {        if($Disposed)            return;        foreach($AIList as my $AICls)            if($AICls->DoUpdate())                return;    }};AIChase.spkThis is the Chase AI it is responsible for making AIs go after the player:Class AIChase extends AI{    my $Path;    my $AIWanderAStar;    my $Chasing;    my $ChaseCount;    my $TryChasePlayer;    my $TryChasePlayerDist;    my $TryChasePlayerLock;    Function __Construct(Entity $E)    {        parent::AI::__construct($E);        // Create a wander ai to use when not chasing a player        // Act like Slime when not chasing a player        $AIWanderAStar = new AIWanderAStar($E);        $AIWanderAStar->$Enabled = true;        // A few chase things        $Chasing = false;        $ChaseCount = 0;        // Keep the path in class for ref access        $Path = array();        // Saves having to resolve a ton of ->        $PathFinder = $Entity->$Game->$Map->$PathFinder;        $Map = $Entity->$Game->$Map;        // Try chase?        $TryChasePlayer = false;        $TryChasePlayerDist = 0;        $TryChasePlayerLock = false;    }    Function DoUpdate( )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        // Find a player to chase after        my $Player = $Entity->FindClosestPlayer(true);        if($TryChasePlayer) // Try chase a player that got too close first        {            if($Player != null)                return TryChasePlayer($Player, $TryChasePlayerDist, $TryChasePlayerLock);            else                return false;        }        if($Player == null)            // If we have nothing to attack just behave like a Wander            return $AIWanderAStar->ASTarWanderAround();        return ChasePlayer($Player, true);    }    Function ChasePlayer( $Player, $AllowWander )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        // If its possible to move begin organizing the chase        if($Entity->Stopped())        {            // Get our current position            my List($X, $Y) = $Entity->GetPosition();                       // Get player position            my List($PX, $PY) = $Player->GetPosition();            // Plot a path to the players position            $Path = array();            $PathFinder->FindPath($Path, $X, $Y, $PX, $PY);            my $Pos = $Path[0];            if($Pos ~~ Array && count($Pos) == 2)            {                // Turn to face player                if($X > $PX)                    $Entity->$Facing = Facing->$Left;                if($X < $PX)                    $Entity->$Facing = Facing->$Right;                // Get the postion we must move to                my $MX = $Pos[0];                my $MY = $Pos[1];                // Move to new postion                my $Moved = false;                if(!$Moved && $MX > $X)                    $Moved = $Entity->TryMove(Direction->$Right);                if(!$Moved && $MX < $X)                    $Moved = $Entity->TryMove(Direction->$Left);                if(!$Moved && $MY > $Y)                    $Moved = $Entity->TryMove(Direction->$Down);                if(!$Moved && $MY < $Y)                    $Moved = $Entity->TryMove(Direction->$Up);                if(!$Moved && $AllowWander)                    return $AIWanderAStar->ASTarWanderAround();            }            else if($AllowWander)                return $AIWanderAStar->ASTarWanderAround();        }        return false;    }    Function TryChasePlayer($Player, $ChaseRange, $UseChaseLocker = false)    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        // We should chase the player?        if($Entity->Stopped())        {            if($AIChase == null)            {                $AIChase = new AIChase($Entity);                $AIChase->$Enabled = true;            }            my List($X, $Y) = $Entity->GetPosition();            my List ($PX, $PY) = $Player->GetPosition();            my $Dist = Utils::Distance2D($X, $Y, $PX, $PY);            if($Dist <= $ChaseRange || $Chasing)            {                if(!$Chasing)                {                    $Chasing = true;                    $ChaseCount = $ChaseRange + 1;                }                else                    $ChaseCount--;                if($ChaseCount > 0)                {                    if($Dist <= $ChaseRange && $ChaseCount == 1)                        $ChaseCount++; // Keep up the chase                    $AIChase->ChasePlayer($Player, false);                    return !$Entity->Stopped(); // Stopped means chase AI didnt move us                }                else                {                    $ChaseCount = 0;                    $Chasing = false;                }            }        }        return false;    }};AIMoveDelay.spkThis tries to make the AIs freeze for a few hundred milliseconds every so often to give the player a chance to escape an AI in hot pursuit this Delay is only used on proper Chase AIs:Class AIMoveDelay extends AI{    my $MoveFor;    my $StopFor;    my $MoveCount;    my $Move;    Function __Construct(Entity $E)    {        parent::AI::__construct($E);        $Move = false;        $MoveCount = 0;        $MoveFor = 0;        $StopFor = 0;    }    Function DoUpdate( )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        if(($Move && $MoveCount == $StopFor) || (!$Move && $MoveCount == $MoveFor))        {            $MoveCount = 0;            $Move = !$Move;        }        $MoveCount++;        if(!$Move)            return false;        return true;    }};AIAttackPlayersInSight.spkThis should cause an AI to immediately start moving towards a player if it gets a clear line of sight:Class AIAttackPlayersInSight extends AI{    Function __Construct(Entity $E)    {        parent::AI::__construct($E);    }    Function DoUpdate( )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        return AttackPlayersInSight();    }    Function AttackPlayersInSight()    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        if($Entity->Stopped())        {            my List($X, $Y) = $Entity->GetPosition();            foreach($Entity->$Game->$Map->Players() as my $V)            {                if(!$V->$BeenHit && !$V->$Immortal)                {                    my List ($PX, $PY) = $V->GetPosition();                    // Horizontal check                    if($Y == $PY)                    {                        if(!$Entity->$Game->$Map->IsPathXBlocked($Y, $X, $PX))                        {                            my $moved = false;                            if($X > $PX)                                $moved = $Entity->TryMove(Direction->$Left);                            else                                $moved = $Entity->TryMove(Direction->$Right);                            return $moved;                        }                    }                    // Verticle check                    if($X == $PX)                    {                        if(!$Entity->$Game->$Map->IsPathYBlocked($X, $Y, $PY))                        {                            my $moved = false;                            if($Y > $PY)                                $moved = $Entity->TryMove(Direction->$Up);                            else                                $moved = $Entity->TryMove(Direction->$Down);                            return $moved;                        }                    }                }            }        }        return false;    }};AIWander.spkThis is the default Wander AI that makes an AI just zip around the map in a fairly clockwise way but with potential randomness:Class AIWander extends AI{    my $AllowRandomMoves; // Used only for non A*    my $GoRandomMove; // Used by $AllowRandomMoves when being called again    Function __Construct(Entity $E)    {        parent::AI::__construct($E);        $AllowRandomMoves = false;        $GoRandomMove = false;    }    Function DoUpdate( )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        // If we arent wandering then we must fix it        // Rules should be:        // 1. Try to avoid going back the way we came        // 2. Always try to go clockwise        // Very simple and basic for the lowest level monster        return WanderAround();    }    Function WanderAround( )    {        if($Entity->Stopped())        {            // This random change of direction is not part of $AllowRandomMoves            // It is just something so the ghost can go places a bit better            if($Entity->CanMove($Entity->$MoveDirection))            {                // 70% chance to continue on path                if(Utils::ChanceOf(0, 100, 0, 70)) // 70% chance to continue on path                    return $Entity->TryMove($Entity->$MoveDirection);            }            my $AvoidDir = Entity::$DirectionOpposit[$Entity->$MoveDirection];            $GoRandomMove = false;            if(!DoMove($AvoidDir, false))                return DoMove($AvoidDir, true);            else                return true;        }    }    Function DoMove( $AvoidDir, $UseAvoidDir )    {        if(!$Enabled)            return false;         if($Disposed)            return false;        if($AllowRandomMoves)        {            if($GoRandomMove || Utils::ChanceOf(0, 100, 20, 40)) // 20% chance to make a totally random move            {                my $moved = false;                $GoRandomMove = true;                // But will still respect the avoid dir                switch((int)Random(0, 3))                {                    case 0:                        if($AvoidDir != Direction->$Up || $UseAvoidDir)                            $moved = $Entity->TryMove(Direction->$Up);                        break;                    case 1:                        if($AvoidDir != Direction->$Down || $UseAvoidDir)                            $moved = $Entity->TryMove(Direction->$Down);                        break;                    case 2:                        if($AvoidDir != Direction->$Left || $UseAvoidDir)                            $moved = $Entity->TryMove(Direction->$Left);                        break;                    default:                        if($AvoidDir != Direction->$Right || $UseAvoidDir)                            $moved = $Entity->TryMove(Direction->$Right);                        break;                }                return $moved;            }        }        if(($AvoidDir != Direction->$Up || $UseAvoidDir) && $Entity->TryMove(Direction->$Up))            return true;        if(($AvoidDir != Direction->$Right || $UseAvoidDir) && $Entity->TryMove(Direction->$Right))            return true;        if(($AvoidDir != Direction->$Down || $UseAvoidDir) && $Entity->TryMove(Direction->$Down))            return true;        if(($AvoidDir != Direction->$Left || $UseAvoidDir) && $Entity->TryMove(Direction->$Left))            return true;        return false;    }};AIWanderAStar.spkThis is a random AI that will move to any location on the map using A* to plot a path to get there:Class AIWanderAStar extends AI{    my $Path;    my $Fails;    my $PathFinder;    my $Map;    Function __Construct(Entity $E)    {        parent::AI::__construct($E);        // To handle random generation failing        $Fails = 0;        // Keep the path in class for ref access        $Path = array();        // Saves having to resolve a ton of ->        $PathFinder = $Entity->$Game->$Map->$PathFinder;        $Map = $Entity->$Game->$Map;    }    Function DoUpdate( )    {        if(!$Enabled)            return false;        if($Disposed)            return false;        if(!AI::$AIEnabled)            return false;        return ASTarWanderAround();    }    Function ASTarWanderAround( )    {        if($Entity->Stopped())        {            my List($X, $Y) = $Entity->GetPosition();            // Generate a new path if needed            if(!$Path)                GenerateNewPath($X, $Y);            // Move to next part of the path            my $Cord = Shift($Path);            if($Cord)            {                if($Cord ~~ Array && count($Cord) == 2)                {                    // Get the postion we must move to                    my $MX = $Cord[0];                    my $MY = $Cord[1];                    if($Map->IsXYBlocked($MX, $MY, true))                    {                        GenerateNewPath($X, $Y);                        return false;                    }                    // Move to new postion                    my $Moved = false;                    if(!$Moved && $MX > $X)                        $Moved = $Entity->TryMove(Direction->$Right);                    if(!$Moved && $MX < $X)                        $Moved = $Entity->TryMove(Direction->$Left);                    if(!$Moved && $MY > $Y)                        $Moved = $Entity->TryMove(Direction->$Down);                    if(!$Moved && $MY < $Y)                        $Moved = $Entity->TryMove(Direction->$Up);                    if($Moved)                    {                        $Fails = 0;                        return true;                    }                    else                        $Fails++;                    if($Fails > 3)                    {                        GenerateNewPath($X, $Y);                        return false;                    }                }            }        }        return false;    }    Function GenerateNewPath($X, $Y)    {        $Path = array();        my $Pos = $Map->FindEmptyCoordinate();        if($Pos != null)            $PathFinder->FindPath($Path, $X, $Y, $Pos[0], $Pos[1]);    }};Utils.spkclass Utils{    Static Function Distance2D($dX0, $dY0, $dX1, $dY1)    {        return Sqrt(($dX1 - $dX0)*($dX1 - $dX0) + ($dY1 - $dY0)*($dY1 - $dY0));    }    Static Function ChanceOf($Min, $Max, $From, $To)    {        my $Chance = (int)Random($Min, $Max + 1);        return $Chance >= $From && $Chance <= $To;    }};
Full clone of the game Pengo
game;ai;sputnik