Programowanie w języku Delphi » Gotowce

Bardzo prosty algorytm SI (Sztucznej Inteligencji), poprawiona wersja

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.

<image>http://zul-84.w.interia.pl/automat_skonczony2.jpg</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

Borek 2005-10-03 00:10

Lepsza sztuczna inteligencja niż ludzka głupota.

wojtek_dodo 2005-08-12 21:03

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

TeWuX 2005-08-12 17:41

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

LOSMARCELOS 2005-08-12 16:07

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!

dArO 2005-08-12 13:52

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

migajek 2005-08-12 12:50

@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...

Marooned 2005-08-12 12:31

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

wro 2005-08-12 08:58

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

LOSMARCELOS 2005-08-11 23:39

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.

necrokris 2005-08-11 22:08

To raczej gotowiec, a nie artykul...

CyberKid 2005-08-11 21:04

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

Deti 2005-08-11 21:03

Do SI to chyba daleko temu..

migajek 2005-08-11 20:06

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

dArO 2005-08-11 19:27

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 :]

Mabakay 2005-08-11 18:32

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);

batas2 2005-08-11 17:48

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?? :|

abc 2005-08-11 17:01

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 ;]