Читайте также: |
|
// ВКЛЮЧАЕМЫЕ ФАЙЛЫ /////////////////////////////////////
#include <stdio.h>
#include <graph.h>
#include <math.h>
// ОПРЕДЕЛЕНИЯ //////////////////////////////
#define STATE_CHASE 1
#define STATE_RANDOM 2
#define STATE_EVADE 3
#define STATE_PATTERN 4
// ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ///////////////////////
// Указатель на системную переменную, содержащую значение таймера.
// Содержимое этой 32-битовой ячейки обновляется 18.2 раза
//в секунду
unsigned int far *clock=(unsigned int far *)0х0000046C;
// Х- и Y-компоненты шаблонов траекторий, по которым
// будет двигаться "Муха"
int patterns_x[33[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,-l,-l,-l,-l,-l, 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};
///////////////////////////////////
void Timer(int clicks)
{
//Эта функция использует значение таймера для формирования
// задержки. Необходимое время задержки задается в "тиках"
// интервалах в 1/18.2 сек. Переменная, содержащая 32-битовое
// текущее значение системного таймера, расположена
// по адресу 0000:046Ch
unsigned int now;
// получить текущее время
now = *clock;
// Ничего не делать до тех пор. пока значение таймера не
// увеличится на требуемое количество "тиков".
// Примечание: один "тик" соответствует примерно 55мс.
while(abs(clock - now) < clicks){}
} // конец функции Timer
// ОСНОВНАЯ ФУНКЦИЯ /////////////////////////////////
void main(void)
{
int px=160,py=100, // начальные координаты игрока
ex=0,ey=0; // начальные координаты противника
int done=0, // флаг окончания работы программы
doing_pattern=0, // флаг выполнения шаблона
current_pattern, // номер текущего шаблона
pattern element, // текущая команда шаблона
select_state=0, // флаг необходимости смены состояния
clicks=20, // количество циклов, в течение которых
// сохраняется текущее состояние
fly_state = STATE CHASE; // "Муха" начинает с преследования
float distance; // используется при расчете
// расстояния между игроком и "Мухой"
_setvideomode(_MRES256COLOR);
printf(" Brainy Fly - Q to Quit");
//основной игровой цикл
while(!done)
{
// очистка точек
_setcolor(0);
_setpixel(px,py);
_setpixel(ex,ey);
// перемещение игрока
if (kbhit()) {
// определяем направление движения
switch(getch()}
{
case 'u': // вверх
{
py-=2;
} break;
case 'n': // вниз
{
py+=2;
} break;
case 'j': // вправо
{
px+=2;
} break;
case 'h'': // влево
{
px-=2;
} break;
case 'q':
{ done=l;
} break;
} // конец оператора switch
}
// конец обработки нажатия клавиши
// теперь перемещаем противника
// начинается работа "мозга"
// определяем текущее состояние КА
switch(fly_state)
{
case STATE_CHASE:
{
_settextposition(24,2);
printf("current state:chase "};.
// реализуем Алгоритм Преследования
if (px>ex) ex++;
if (px<ex) ex--;
if (py>ey) ey++;
if (py<ey) ey--;
//не пора ли перейти к другому состоянию?
if (--clicks==0) select_state=l;
} break;
case STATE_RANDOM:
(
_settextposition(24,2};
printf("current state:random ");
// перемещаем "Муху" в случайном направлении
ex+=curr_xv;
ey+=curr_yv;
//не пора ли перейти к другому состоянию?
if (--clicks=0) select_state=l;
} break;
case STATE_EVADE:
{
_settextposition(24,2);
printf("current state:evade ");
// реализуем Алгоритм Уклонения
if (px>ex) ex--;
if (px<ex) ex++;
if (py>ey) ey—;
if (py<ey) ey++;
//не пора ли перейти к другому состоянию?
if (--clicks==0) select_state=l;
} break;
case STATE_PATTERN:
{
_settextposition(24,2);
printf("current state:pattern ");
// перемещаем "Муху", используя следующий
// элемент текущего шаблона
ex+=patterns_x[current_pattern][pattern_element];
ey+=patterns_y[current_pattern][pattern_element];
// обработка шаблона закончена?
if (++pattern element==20)
{
pattern_element = 0;
select_state=l;
} // конец проверки завершения шаблона
} break;
default;break;
} // конец обработки текущего состояния
// надо ли менять, состояние?
if (select_state==l)
{
// Выбор нового состояния основан на
// игровой ситуации и неявной логике.
// Для выбора используется расстояние до игрока
distance = sqrt(.5+fabs((рх-ех)*(рх-ех)+(ру-еу)*(ру-еу)));
if (distance>5&&distance<15&&rand()%2==1)
{
// выбираем новый шаблон
current_pattern = rand()%3;
// переводим "мозг" в состояние действий по шаблону
fly_state = STATE_PATTERN;
pattern_element = 0;
} // конец обработки ситуации "близко к игроку"
else if (distance<10) // слишком близко, убегаем!
clicks=20;
fly_state = STATE_EVADE;
} // конец обработки ситуации "слишком близко"
else
if (distance>25&sdistance<100&&rand()%3==1)
{
// преследуем игрока clicks=15;
fly_state = STATE_CHASE;
} // конец обработки ситуации Преследование
else
if (distance>30&&rand()%2==l)
{
clicks=10;
fly_State = STATE_RANDOM;
curr_xv = -5 + rand()%10; //от -5 до +5
curr_yv = -5 + rand()%10; //от -5 до +5
} // конец "неявной логики"
else
{
clicks=5;
fly_state = STATE_RANDOM;
curr_xv = -5 + rand()%10; //от -5 до +5
curr_yv = -5 + rand()%10; //от -5 до +5
} // конец оператора else // сбрасываем флаг смены состояния
select_state=0;
} // конец Алгоритма Смены Состояния
// Убеждаемся, что "Муха" находится в пределах экрана
if (ex>319) ех=0;
i£ (ex<0) ех=319;
if (ey>199) еу=0;
if (ey<0) еу=199;
// конец работы "мозга"
_setcolor(12);
_setpixel(ex,ey);
// Немного подождем...
Timer(1);
} // Конец цикла while
_setvideomode(_DEFAULTMODE);
} // Конец функции main
Запустив программу, вы наверняка обратите внимание на кажущуюся сложность поведения «Мухи». А ведь для получения такого результата использована весьма простая схема! Возможно, и вы заметите то, что увидел я - чуть ли не человеческую способность мыслить.
Управление приоритетным состоянием
Можно еще усовершенствовать наш КА, если ввести управление сменой состояний с помощью некоторых переменных и функций. На практике это означает возможность изменения текущего режима, не дожидаясь полного завершения программы, отвечающей за его выполнение.
В имитаторе движения «Мухи» каждое состояние выполнялось до полного завершения. Но если включить в программу проверку выполнения или невыполнения некоторых условий, это позволило бы КА «выпрыгнуть» из состояния, не дожидаясь окончания его «отработки».
Этим заканчивается наш разговор о конечных автоматах, которые могут быть использованы в наших играх для моделирования поведения существ и придания им видимости наличия интеллекта. Теперь к имеющейся у нас системе высокоуровневого управления не помешает добавить низкоуровневое функционирование.
Вероятностные автоматы
Наверное, вы уже поняли, как вероятность и случайные числа могут быть использованы для выбора направлений и состояний. Мы научились использовать случайные последовательности для конструирования «характера» персонажей. Я имею в виду, что «Муха» в нашем предыдущем примере могла самостоятельно выбирать различные состояния, основываясь на окружающей обстановке. Если несколько изменить метод выбора состояний, основанны на генерации случайных чисел (то есть, создать условия, при которых вход в опредёленное состояние стал бы легче или тяжелее), то, в конечном счете, нам удалось бы изменить "характер" «Мухи».
Скажем, нам захотелось иметь в игре две «мухи». Если одну и ту же программу использовать для создания траектории движения каждой «мухи», они бы действовали одинаково. Во многих случаях большего и не требуется. Однако гораздо интересней иметь много «мух» с небольшими различиями в поведении. Это можно было бы реализовать изменением диапазона случайных чисел-во всех строках программы, где выбираются состояния «мухи». Но такой подход будет очень грубым. Мы пойдем другим путем — путем создания общего метода управления характером персонажей, основанного на вероятности.
В искусственном интеллекте «индивидуальность» означает возможность существ по-разному выполнять определенные действия при одних и тех же обстоятельствах. Например, у меня есть несколько достаточно активных друзей, которые захотели бы слегка проучить плута, попытавшегося их надуть. Но у меня также есть друзья, которые более спокойны и предпочитают сначала думать, а потом действовать. Скорее всего, мошеннику удалось бы как-то с ними договориться. То, что мы видим на данном примере и является «индивидуальностью». Каким именно способом это будет достигнуто, не принципиально, важен конечный результат.
В видеоиграх мы могли бы иметь несколько противников, которые постоянно преследуют нас, пока другие в это время неподвижны и стреляют. Третьи трусливы и предпочитают убегать, а не сражаться. Анализируя ситуацию, мы видим, что имеется все тот же набор состояний, но вероятности перехода в них различны для каждого создания. Для моделирования этого процесса мы могли бы сослаться на «таблицу вероятностей» в той части программы, которая выбирает новое состояние. Эта таблица могла бы содержать различные степени вероятности переходов существ в то или иное состояние. К примеру, взгляните на таблицу 13.1, в которой перечислены соотношения вероятностей для трех различных существ в одной игре.
Таблица 13.1. Распределение вероятностей для трех существ в игре.
Дата добавления: 2015-07-12; просмотров: 102 | Нарушение авторских прав
<== предыдущая страница | | | следующая страница ==> |
Листинг 13.3. Одинокая муха. (DFLY.C). | | | Состояние Существо 3: Хныкалка |