Bardzo prosty algorytm SI (Sztucznej Inteligencji), poprawiona wersja

LOSMARCELOS

Potrzebne są 3 komponenty
PLAYER_IMAGE1
ENEMY_IMAGE1
oraz Timer1

Ten kod prezentuje algorytm pościugu.
Za graczem PLAYER_IMAGE1 bedzie podążać ENEMY_IMAGE1 (przeciwnik).
Ikonki, bitmapy na komponentach możemy sobie wczytać jakie chcemy.

unit INTEL_W1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls;

type
  TForm1 = class(TForm)
    Player_Image1: TImage;
    Enemy_Image1: TImage;
    Timer1: TTimer;
    procedure FormKeyDown(Sender: TObject; var Key: Word;
      Shift: TShiftState);
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
Application.ProcessMessages;

{obsluga klawiatury}
if Key = VK_LEFT then
    Player_Image1.Left := Player_Image1.Left - 2; // jesli kursor w lewo - przesun gracza w lewo

if Key = VK_RIGHT then
    Player_Image1.Left := Player_Image1.Left + 2; // jesli kursor w prawo - przesun gracza w prawo

if Key = VK_UP then
    Player_Image1.Top := Player_Image1.Top - 2; // jesli kursor do gory - przesun gracza do gory

if Key = VK_DOWN then
    Player_Image1.Top := Player_Image1.Top + 2; // jesli kursor do dolu - przesun gracza do dolu

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
DoubleBuffered := True; // zalacz podwojne buforowanie,
                        // w celu unikniecia migotania obrazkow przy przesuwaniu
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
// Mechanizm naszej sztucznej prymitywnej inteligencji
if Player_Image1.Left > Enemy_Image1.Left then
Inc(Enemy_Image1.Left);

if Player_Image1.Left < Enemy_Image1.Left then
Dec(Enemy_Image1.Left);

if Player_Image1.Top > Enemy_Image1.Top then
Inc(Enemy_Image1.Left);

if Player_Image1.Top < Enemy_Image1.Top then
Dec(Enemy_Image1.Left);

{
tak ten kod wyglądal pierwotnie w C
     if (px>ex) ex++;
     if (px<ex) ex--;
     if (py>ey) ey++;
     if (py<ey) ey--;         }

end;

end.

Poprawiłem błąd.

===============================================

Automaty skończone zależne od stanu otoczenia.

user image

(wymagany kompilator MS C/C++ 7.0, jak bede mieć czas to zrobie konwersje na Borland C++ i BGI, a może później na C++ Buildera lub Delphi).

Tylko nie wiem gdzie umieścić artykuł, bo częśc jest w Delphi, część w C/
Może do kategori "Z pogranicza" ?


// MODUŁY /////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <graph.h>
#include <math.h>

// DEFINICJE  /////////////////////////////////////////////////////////////////

#define STATE_CHASE   1
#define STATE_RANDOM  2
#define STATE_EVADE   3
#define STATE_PATTERN 4

// ZMIENEN GLOBALNE ////////////////////////////////////////////////////////////

unsigned int far *clock = (unsigned int far *)0x0000046C; // wskaźnik na zegar wewnętrzny

// składowe x i y wzoru w postaci 2-wymiarowych tablic

int patterns_x[3][20]= { 1,1,1,1,1,2,2,-1,-2,-3,-1,0,0,1,2,2,-2,-2,-1,0,
                         0,0,1,2,3,4,5,4,3,2,1,3,3,3,3,2,1,-2,-2,-1,
                         0,-1,-2,-3,-3,-2,-2,0,0,0,0,0,0,1,0,0,0,1,0,1 };

int patterns_y[3][20] = { 0,0,0,0,-1,-1,-1,-1,-1,0,0,0,0,0,2,2,2,2,2,2,
                          1,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,0,0,0,0,
                          1,1,1,2,2,-1,-1,-1,-2,-2,-1,-1,0,0,0,1,1,1,1,1 };

//////////////////////////////////////////////////////////////////////////////

Timer(int clicks)
{
//funkcja wykorzystuje wewn. zegar do uzyskania przerwy w programie

unsigned int now;

// odczytaj aktualny czas

now = *clock;

//każde tyknięcie zegara to ok 55 ms

while(abs(*clock - now) < clicks){}

} /

// M A I N ///////////////////////////////////////////////////////////////////

main()
{

int px=160,py=100,    // pozycja startowa gracza
    ex=0,ey=0,        // pozycja startowa przeciwnika
    curr_xv,curr_yv;  // prędkość "muchy" podczas wykonywania chaotycznych ruchów

int done=0,           // znacznik wyjscia
    doing_pattern=0,  // znaczniki odtwarzania wzoru
    current_pattern,  // aktualnie odtwarzany wzor
    pattern_element,  // aktualnie wykonywany element wzoru
    select_state=0,   // zmiana stanu
    clicks=20,        // liczba "cykli"
    fly_state = STATE_CHASE;  // od jakiego stanu sie zaczyna ruch much - STATE CHASE - poscig

float distance;       // odleglosc (dystans) miedzy mucha a graczem

_setvideomode(_MRES256COLOR);

printf(" Nacisnij Q aby wyjsc");

// glowna petla gry

while(!done)
     {
     // wymazanie kropek

     _setcolor(0);

     _setpixel(px,py);
     _setpixel(ex,ey);

     // przesun gracza

     if (kbhit())
        {

        // ruch gracza, na podstawie klawiszy

        switch(getch())
              {

              case 'u': // GÓRA
                      {
                  py-=2;
                      } break;

              case 'n': // DÓŁ
                      {
                  py+=2;
                      } break;

              case 'j': // PRAWA
                      {
                  px+=2;
                      } break;

              case 'h': // LEWA
                      {
                  px-=2;
                      } break;

              case 'q':
                      {
                      done=1;
                      } break;

              } // koniec bloku switch

        } // koniec bloku if

     // przesuń przeciwnika

     // w jakim stanie jest "mózg" przeciwnika

     switch(fly_state)
           {

           case STATE_CHASE:
                {
                _settextposition(24,2);
                printf("Biezacy stan : poscig   ");

                // mucha goni gracza

                if (px>ex) ex++;
                if (px<ex) ex--;
                if (py>ey) ey++;
                if (py<ey) ey--;

                // przejscie do innego stanu

                if (--clicks==0)
                   select_state=1;

                } break;

           case STATE_RANDOM:
                {
                _settextposition(24,2);
                printf("Biezacy stan : ruchy chaotyczne  ");

                // mucha porusza sie chaotycznie

                ex+=curr_xv;
                ey+=curr_yv;

                // czas na inny stan

                if (--clicks==0)
                   select_state=1;

                } break;

           case STATE_EVADE:
                {
                _settextposition(24,2);
                printf("Biezacy stan : ucieczka  ");

                // mucha ucieka od gracza

                if (px>ex) ex--;
                if (px<ex) ex++;
                if (py>ey) ey--;
                if (py<ey) ey++;

                // czas na nastepny stan

                if (--clicks==0)
                   select_state=1;

                } break;

           case STATE_PATTERN:
                {
                _settextposition(24,2);
                printf("Biezacy stan : wzór ");

                // mucha porusza sie wg wzoru

                ex+=patterns_x[current_pattern][pattern_element];
                ey+=patterns_y[current_pattern][pattern_element];

                // zakonczono odtwarzanie wzoru

                if (++pattern_element==20)
                    {
                    pattern_element = 0;
                    select_state=1;
                    } // koniec bloku if

                } break;

           default:break;

           } // koniec bloku switch

           // czy stan powinien ulec zmianie ?

           if (select_state==1)
              {
                    // wybranie stanu korzystajac ze stanu otoczenia
               // i korzystając z "logiki rozmytej"
               // wykorzystanie odległości gracza d wybrania funkcji nowego stanu

              distance =  sqrt(.5 + fabs((px-ex)*(px-ex) + (py-ey)*(py-ey)));

              if (distance > 5 && distance <15 && rand()%2==1)
                 {
                 // losowe wybranie wzoru

                 current_pattern = rand()%3;

                 // wprowadzenie "mózgu" w stan odtwarzania wzoru

                 fly_state = STATE_PATTERN;

                 pattern_element = 0;

                 } // koniec bloku if
              else
              if (distance < 10) // uciekamy :-) !
                 {
                 clicks=20;
                 fly_state = STATE_EVADE;

                 } // mucha sciga gracza !
              else
              if (distance > 25 && distance <100 && rand()%3==1)  // let's chase player
                 {
                 clicks=15;
                 fly_state = STATE_CHASE;

                 }  // koniec bloku if
              else
              if (distance > 30 && rand()%2==1)
                 {
                 clicks=10;
                 fly_state = STATE_RANDOM;

                 curr_xv = -5 + rand()%10; // -5 to +5
                 curr_yv = -5 + rand()%10; // -5 to +5

                 } // koniec bloku if
              else
                 {
                 clicks=5;
                 fly_state = STATE_RANDOM;

                 curr_xv = -5 + rand()%10; // -5 to +5
                 curr_yv = -5 + rand()%10; // -5 to +5

                 } // koniec bloku else

              // wyzerowanie znacznika zmiany stanu

              select_state=0;

              } // koniec bloku if

     // sprawdzenie, czy mucha pozostaje na ekranie

     if (ex>319) ex=0;
     if (ex<0)   ex=319;
     if (ey>199) ey=0;
     if (ey<0)   ey=199;

     // rysowanie kropek

     _setcolor(9);
     _setpixel(px,py);

     _setcolor(12);
     _setpixel(ex,ey);

     // momencik przerwy

     Timer(1);

     } // koniec pętli while

_setvideomode(_DEFAULTMODE);

} // koniec funkcji main

Literatura w której jest opisana SI (ujęta z różnych aspektów)

LaMothe, Ratcliff, Seminatore & Tyler, Sztuczki i tajemnice programowania gier, Warszawa, 1996.

J.Grębosz - Symfonia C++, Kraków, 2002.

Cichosz P., Systemy uczące się, WNT, Warszawa, 2000.

Ziembiński Z. Logika Praktyczna, PWN, Warszawa, 1974.

Pozdrawiam! :-)

17 komentarzy

if Player_Image1.Left > Enemy_Image1.Left then
inc(Enemy_Image1.Left);
if Player_Image1.Left < Enemy_Image1.Left then
dec(Enemy_Image1.Left);
if Player_Image1.Top > Enemy_Image1.Left then
inc(Enemy_Image1.Top)
if Player_Image1.Top < Enemy_Image1.Left then
dec( Enemy_Image1.Top );

Jakoś tak mniej zajmuje :]
Ale ogÓlnie jak by to powiedzieli gracze GG ;]

Lepsza sztuczna inteligencja niż ludzka głupota.

Jaki zaawansowany kod.... :-)
Szczególnie to "SI"....

do komentarza abc:
inc() i dec() nie tylko mniej zajmuje, ale i szybciej jest wykonywane przez procesor :]

Pierwotnie kod był nie w C++ ale czystym C dla DOS (dokładniej przy użyciu kompilatora Microsoft C/C++ 7.0).
Ja zrobiłem konwersje dla Delphi pod Windows.
W załączniku jest oryginał z 1994 roku w C.
Jak już mówiłem, później wrzuce bardziej zaawansowany algorytm.

Pozdrawiam!

no i pousuwaj jeszcze chociaz te beginy i endy :/ troche ich za duzo... niepotrzebnie zajmuja miejsce (na stronie) :D

@Marooned:
5: ten kod jest czystym tlumaczeniem na Delphi z jakiejs innej strony - doskonale pamietam ten kod w C++ tylko nie moge se przypomniec linka...

Wnoszę o usunięcie tego - powody:
1) to mógłby być co najwyżej wstęp o zarysie historycznym SI a nie cały art
2) nie ma to nic wspólnego z SI
3) poziom merytoryczny jest zbyt niski
4) opisana obsługa klawiszy może zostać zostosowana przez początkujących - a my tu raczej chcemy, aby ludzie pisali dobre programy

artykuł ciekawy, tylko co to ma wspólnego z SI ?

Poprawiłem błąd, dodałem źródło + exe, oraz kod pierwowzoru w C, exe w C pod dosa ;)
Poza tym to miałbyć prosty algorytm więc nie oczekujcie super inteligencji. Może później zrobie coś bardziej skomplikowanego.

To raczej gotowiec, a nie artykul...

ales narobil SI i po co te begin ... end ??
btw mowisz ze bierzesz sie za pisanie gier wiec podpowiem Ci, ze zle rozwiazales sprawe z klawiszami. Tworzysz tablice klawiszy gdzie masz stany keydown i keyup i potem sprawdzasz je w zdarzeniu ontimer. Dzieki temu Twoja postac ma okreslona predkosc

Do SI to chyba daleko temu..

hmm... sry, ale ja juz to gdzies widzialem :] Nie pamietam dokladnie gdzie ale takie cos bylo...

jest blad w kodzie:
if Player_Image1.Top > Enemy_Image1.Left then
if Player_Image1.Top < Enemy_Image1.Left then

przyrownujesz top do left...
powinno byc
if Player_Image1.Top > Enemy_Image1.Top then
if Player_Image1.Top < Enemy_Image1.Top then

a tak to moze poczatkujacych cos natchnie :]

if Player_Image1.Left > Enemy_Image1.Left then inc(Enemy_Image1.Left)
else if Player_Image1.Left < Enemy_Image1.Left thendec(Enemy_Image1.Left)
else if Player_Image1.Top > Enemy_Image1.Left theninc(Enemy_Image1.Top)
else if Player_Image1.Top < Enemy_Image1.Left thendec(Enemy_Image1.Top);

Popieram abc, coraz wiecej ludzi pisze te nie potrzebne beginy i endy czy { } w przypadku c/c++ (w kojocie to samo :| ), pytanie jest po co?? :|