Let the Grokk Be
Рубрика: Development | 22 December 2006, 13:35 | juriy
По заявкам трудящихся: как сделать простого зверя.
В этой статье описан процесс создания простого зверька для онлайн соревнования искусственных интеллектов: Электро Джунгли. Детальнее про то, что это за проект можно на официальном сайте: http://www.electricjungle.ru или прочитать в моем маленьком обзоре http://voituk.kiev.ua/2006/12/22/elecrojungle-hunters-twilight/
Я использую Eclipse, поэтому я буду описывать весь “процесс” именно на примере этой IDE.
Итак, для начала необходимо подготовить среду разработки.
1. Создаем новый Java проект.
2. Добавляем в Build Path библиотеку, которую скачали с сайта (для тех, кто еще не скачал – можно взять тут: http://www.electricjungle.ru:8080/main.jsp?page=sdk).
3. Создаем новый package. Создаем в этом package новый класс, который будет описывать логику поведения зверя. В моем примере я буду использовать пакет ua.net.lab.beings, а класс будет называться Grokk. Класс должен реализовывать интерфейс universum.bi.Being.
4. После создания и автоматической генерации заглушек для абстрактных классов код выглядит так:
[java]
package ua.net.lab.beings;
import universum.bi.Being;
import universum.bi.BeingInterface;
import universum.bi.BeingParams;
import universum.bi.Event;
import universum.bi.UserGameInfo;
public class Grokk implements Being {
public String getName() {
return null;
}
public String getOwnerName() {
return null;
}
public BeingParams getParams() {
return null;
}
public void reinit(UserGameInfo info) {
}
public Event makeTurn(BeingInterface world) {
return null;
}
public void processEvent(BeingInterface bi, Event e) {}
}
[/java]
Теперь нужно заполнить заглушки кодом. Начнем с самого простого. Функции getName() и getOwnerName() возвращают, соответственно имя зверюшки и имя создателя.
[java]
public String getName() {
return "Grokk";
}
public String getOwnerName() {
return "Juriy";
}
[/java]
Дальше идет функция getParams() – она определяет начальные параметры первого существа (параметры следующих поколений могут быть другими): его массу и скорость. Эти параметры лучше сразу вынести в константы: их придется подбирать экспериментально. Поскольку менять легче в одном месте, то так и поступим:
[java]
private final static float MASS = 10f;
private final static float SPEED = 2f;
....
public BeingParams getParams() {
return new BeingParams(MASS, SPEED);
}
[/java]
Функция reinit(UserGameInfo info) – вызывается при старте. Из нее зверек может узнать базовые сведения про мир. Здесь немного отвлечемся от кода и снова глянем на правила: есть на сайте описано 3 типа игры:
SINGLE – твоя зверюга – одна в джунглях. Цель – получить за ограниченное количество ходов максимальную массу популяции.
DUEL – думаю, без комментариев – в джунглях 2 вида. Победит тот, у кого после определенного количества ходов – максимальная масса популяции.
JUNGLE – то же самое, что и DUEL, только видов может быть до 8.
В файле с описанием констант: universum.bi.GameKind есть описание еще для двух типов игр: PEACE_DUEL и DEBUG. Поскольку комментариев про эти типы игр нет, то я оставляю читателям трактовать значения.
Вернемся к коду. Поскольку существо не настолько умное, чтобы его поведение менялось в зависимости от типа игры, то функцию reinit(UserGameInfo info) оставим пустой. В более продвинутом зверьке в этой функции можно инициализировать static переменные. Функция вызывается один раз, до установки первого зверя на поле.
Дальше – самое интересное. Функция makeTurn(BeingInterface world) – сердце (вернее, мозг) вашего зверя. Тут зверь решает, куда ему пойти.
Параметр, который передается в функцию, позволяет зверю получить данные о мире, который его окружает. Поскольку для принятия решений о действиях эта переменная будет нужна постоянно, лучше ее с самого начала сделать полем класса, и обновлять каждый раз при вызове makeTurn(BeingInterface world).
[java]
private BeingInterface world;
...
public Event makeTurn(BeingInterface world) {
this.world = world;
return null;
}
[/java]
Пускай зверек двигается случайным образом в любую доступную клетку. Для этого я создал отдельную функцию – makeRandomMove(), которая возвращает Event – решение зверька о том, что делать дальше.
[java]
private Event makeRandomMove() {
// Получаем список клеток, в которые можно попасть за ход
List<Location> canReach = world.getReachableLocations(this);
// Выбираем клетку случайным образом и создаем событие - движение
// в выбранную клетку.
return new Event(
EventKind.ACTION_MOVE_TO,
canReach.get(Util.rnd(canReach.size())));
}[/java]
Обратите внимание на Util.rnd(canReach.size()). Авторы не рекомендуют пользоваться java.util.Random для выбора случайных чисел. Util.rnd() - тот же Random только в профиль. Все чем он отличается: в режиме отладки он генерирует одинаковые последовательности случайных чисел.
Теперь остается поменять makeTurn(BeingInterface world) так чтобы зверь двигался в случайном направлении каждый ход.
[java]
public Event makeTurn(BeingInterface world) {
this.world = world;
return makeRandomMove();
}
[/java]
Все что осталось сделать - запаковать зверя в jar и не забыть добавить в MANIFEST.MF строку
Main-Class: ua.net.lab.beings.Grokk
Теперь отпускаем зверя на волю: запускаем эмулятор джунглей:
java -jar ejungle_distr.jar
в GUI добавляем в джунгли нового зверя: Add Being -> Add from jar.
Зверек немного побегает и сдохнет от голода: кушать он не умеет. Но научим мы его позже, сейчас немного поработать надо :)
Вот код того что получилось:
[java]
package ua.net.lab.beings;
import java.util.List;
import universum.bi.Being;
import universum.bi.BeingInterface;
import universum.bi.BeingParams;
import universum.bi.Event;
import universum.bi.EventKind;
import universum.bi.Location;
import universum.bi.UserGameInfo;
import universum.util.Util;
public class Grokk implements Being {
private final static float MASS = 10f;
private final static float SPEED = 2f;
private BeingInterface world;
public String getName() {
return "Grokk";
}
public String getOwnerName() {
return "Juriy";
}
public BeingParams getParams() {
return new BeingParams(MASS, SPEED);
}
public void reinit(UserGameInfo info) {}
public Event makeTurn(BeingInterface world) {
this.world = world;
return makeRandomMove();
}
private Event makeRandomMove() {
// Получаем список клеток, в которые можно попасть за ход
List<Location> canReach = world.getReachableLocations(this);
// Выбираем клетку случайным образом и создаем событие - движение
// в выбранную клетку.
return new Event(
EventKind.ACTION_MOVE_TO,
canReach.get(Util.rnd(canReach.size())));
}
public void processEvent(BeingInterface bi, Event e) {}
}
[/java]
Tweet

обсуждение в тему
http://yorool-gui.livejournal.com/141458.html
Офигительно!
2Скакунов Александр:
Хочу увидеть что ты скажешь когда узнаешь как это выглядит:)