Оболочка для не-Java представлений
ПРИМЕЧАНИЕ Поддержка MAP_SIZE была добавлена. Если вы заботитесь, пожалуйста, обновите вашу заявку соответственно.
Это вики-запись сообщества для обёртки, которую могут использовать те, кто хочет играть, но не любит / не знает Java. Пожалуйста, используйте его, получайте удовольствие, и я рад помочь вам настроить вещи.
Сейчас уже довольно поздно, так как я заканчиваю, поэтому другие Java-кодеры, пожалуйста, просмотрите это и предложите улучшения. Если вы можете, сделайте это через мой репозиторий github, отправив сообщение о проблеме или отправив исправление. Спасибо!
Это все распространяется с UNLICENSE, пожалуйста, следуйте / разветвите его из репозитория github . Присылайте патчи там, если вы обнаружите проблемы, и я обновлю этот пост.
Текущие примеры Wrapper в использовании
plannapus : WolfCollectiveMemory в R
зубная щетка : зубная щетка в ECMAScript
Как пользоваться
Ниже приведены инструкции по протоколу межпроцессного взаимодействия через ТРУБЫ, которые я определил для удаленных волков. Обратите внимание, что я пропустил MAP_SIZE, так как он, кажется, не существует, несмотря на его присутствие в постановке задачи OP. Если он появится, я обновлю этот пост.
ВАЖНЫЕ ЗАМЕЧАНИЯ :
- Будет выполнен только один вызов вашего внешнего процесса (поэтому оберните вашу логику обработки в бесконечный цикл. Это также позволяет вам сохранять любую обработку в памяти вместо использования диска)
- Все общение с этим единственным внешним процессом осуществляется через STDIN и STDOUT.
- Вы должны явно сбросить весь вывод, отправленный в STDOUT, и убедиться, что он завершен символом новой строки
Спецификация
Удаленные сценарии поддерживаются простым протоколом через ловушки STDIN и STDOUT и делятся на инициализацию, перемещение и атаку. В каждом случае связь с вашим процессом будет осуществляться через STDIN, и от STDOUT необходим ответ. Если ответ не получен в течение 1 секунды, ваш процесс будет считаться мертвым, и будет выдано исключение. Все символы будут закодированы в UTF-8 для согласованности. Каждый ввод завершается символом новой строки, и ваш процесс также должен завершать каждый ответ на вывод новой строкой.
ПРЕДУПРЕЖДЕНИЕ Обязательно очищайте выходной буфер после каждой записи, чтобы гарантировать, что оболочка Java увидит ваш вывод. Невозможность выполнить сброс может привести к отказу удаленного волка.
Обратите внимание, что будет создан только один процесс, все Волки должны управляться в этом одном процессе. Читайте дальше, как эта спецификация поможет.
инициализация
STDIN: S<id><mapsize>
\ n
STDOUT: K<id>
\ n
<id>
: 00
или 01
или ... или99
Объяснение:
Символ S
будет отправлен после двух числовых символов 00
, 01
..., 99
указывающих, какой из 100 волков инициализируется. Во всем будущем общении с этим конкретным волком <id>
будет использоваться то же самое .
После идентификатора будет отправлена последовательность числовых символов переменной длины. Это размер карты. Вы узнаете, что последовательность числовых символов заканчивается, когда вы достигнете новой строки ( \n
).
Чтобы убедиться, что ваш процесс жив, вы должны ответить тем же персонажем, K
за которым <id>
вы получили то же самое . Любой другой ответ приведет к исключению, убившему ваших волков.
движение
STDIN: M<id><C0><C1>...<C7><C8>
\ n
STDOUT: <mv><id>
\ n
<Cn>
: W
или
или B
или S
илиL
W
: Волк
: Пустое пространство
B
: Медведь
S
: Камень
L
: Лев
<mv>
: H
или U
или L
или R
илиD
H
: Move.HOLD
U
: Move.UP
L
: Move.LEFT
R
: Move.RIGHT
D
: Move.DOWN
Объяснение:
Персонаж M
будет отправлен после двух символов, <id>
чтобы указать, какой Волк должен выбрать ход. После этого будет отправлено 9 символов, представляющих окружение этого Волка, в порядке строк (верхний ряд, средний ряд, нижний ряд слева направо).
Ответ с одним из действительных символов движения <mv>
, сопровождаемый двумя цифрами Волка <id>
для подтверждения.
Атака
STDIN: A<id><C>
\ n
STDOUT: <atk><id>
\ n
<C>
: W
или B
или S
илиL
<atk>
: R
или P
или S
илиD
R
: Attack.ROCK
P
: Attack.PAPER
S
: Attack.SCISSORS
D
: Attack.SUICIDE
Объяснение:
Персонажу A
отправят два символа, <id>
чтобы указать, какой волк участвует в атаке. За этим следует один символ, <C>
указывающий, на какой тип объекта атакует: W
олф, B
ухо, S
тон или L
ион.
Ответьте одним из <atk>
перечисленных выше символов, указав свой ответ на атаку, за которым следуют две цифры <id>
для подтверждения.
Вот и все. Там нет больше к этому. Если вы проиграете атаку, <id>
она никогда не будет отправлена вашему процессу снова, вот как вы узнаете, что ваш Волк умер - если прошел полный раунд Движения без <id>
отправки.
Заключение
Обратите внимание, что любые исключения убьют всех Волков вашего удаленного типа, так как из вашего удаленного волка создается только один «Процесс» для всех создаваемых волков вашего типа.
В этом хранилище вы найдете Wolf.java
файл. Найдите и замените следующие строки, чтобы настроить своего бота:
Замените <invocation>
аргументом командной строки, который будет правильно выполнять ваш процесс.
Замените <custom-name>
уникальным именем вашего Волка.
Для примера посмотрите на репозиторий , где у меня есть, WolfRandomPython.java
который вызывает мой пример удаленного, PythonWolf.py
(Волк Python 3+).
Переименуйте файл Wolf<custom-name>.java
, который <custom-name>
будет заменен на имя, которое вы выбрали выше.
Чтобы проверить свой Wolf, скомпилируйте программу Java ( javac Wolf<custom-name>.java
) и следуйте инструкциям Рашера, чтобы включить его в программу моделирования.
Важно: Обязательно предоставьте четкие , краткие инструкции о том, как скомпилировать / выполнить ваш настоящий Wolf, следуя схеме, которую я обрисовал выше.
Удачи, и пусть природа всегда будет в твою пользу.
Код Обертки
Помните, что вы ДОЛЖНЫ выполнять поиск и замену, описанную выше, чтобы это работало. Если ваш призыв особенно волосатый, пожалуйста, свяжитесь со мной для помощи.
Обратите внимание, что main
в этой оболочке есть метод, позволяющий элементарное тестирование «пройти / не пройти» на вашем локальном компьютере. Для этого загрузите класс Animal.java из проекта и удалите package animals;
строку из обоих файлов. Замените строку MAP_SIZE в Animal.java некоторой константой (например, 100). Скомпилируйте их, используя javac Wolf<custom-name>.java
команду execute via java Wolf<custom-name>
.
package animals;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.OutputStreamWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
/**
* Remote Wolf<custom-name> wrapper class.
*/
public class Wolf<custom-name> extends Animal {
/**
* Simple test script that sends some typical commands to the
* remote process.
*/
public static void main(String[]args){
Wolf<custom-name>[] wolves = new Wolf<custom-name>[100];
for(int i=0; i<10; i++) {
wolves[i] = new Wolf<custom-name>();
}
char map[][] = new char[3][3];
for (int i=0;i<9;i++)
map[i/3][i%3]=' ';
map[1][1] = 'W';
for(int i=0; i<10; i++) {
wolves[i].surroundings=map;
System.out.println(wolves[i].move());
}
for(int i=0; i<10; i++) {
System.out.println(wolves[i].fight('S'));
System.out.println(wolves[i].fight('B'));
System.out.println(wolves[i].fight('L'));
System.out.println(wolves[i].fight('W'));
}
wolfProcess.endProcess();
}
private static WolfProcess wolfProcess = null;
private static Wolf<custom-name>[] wolves = new Wolf<custom-name>[100];
private static int nWolves = 0;
private boolean isDead;
private int id;
/**
* Sets up a remote process wolf. Note the static components. Only
* a single process is generated for all Wolves of this type, new
* wolves are "initialized" within the remote process, which is
* maintained alongside the primary process.
* Note this implementation makes heavy use of threads.
*/
public Wolf<custom-name>() {
super('W');
if (Wolf<custom-name>.wolfProcess == null) {
Wolf<custom-name>.wolfProcess = new WolfProcess();
Wolf<custom-name>.wolfProcess.start();
}
if (Wolf<custom-name>.wolfProcess.initWolf(Wolf<custom-name>.nWolves, MAP_SIZE)) {
this.id = Wolf<custom-name>.nWolves;
this.isDead = false;
Wolf<custom-name>.wolves[id] = this;
} else {
Wolf<custom-name>.wolfProcess.endProcess();
this.isDead = true;
}
Wolf<custom-name>.nWolves++;
}
/**
* If the wolf is dead, or all the wolves of this type are dead, SUICIDE.
* Otherwise, communicate an attack to the remote process and return
* its attack choice.
*/
@Override
public Attack fight(char opponent) {
if (!Wolf<custom-name>.wolfProcess.getRunning() || isDead) {
return Attack.SUICIDE;
}
try {
Attack atk = Wolf<custom-name>.wolfProcess.fight(id, opponent);
if (atk == Attack.SUICIDE) {
this.isDead = true;
}
return atk;
} catch (Exception e) {
System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
isDead = true;
return Attack.SUICIDE;
}
}
/**
* If the wolf is dead, or all the wolves of this type are dead, HOLD.
* Otherwise, get a move from the remote process and return that.
*/
@Override
public Move move() {
if (!Wolf<custom-name>.wolfProcess.getRunning() || isDead) {
return Move.HOLD;
}
try {
Move mv = Wolf<custom-name>.wolfProcess.move(id, surroundings);
return mv;
} catch (Exception e) {
System.out.printf("Something terrible happened, this wolf has died: %s", e.getMessage());
isDead = true;
return Move.HOLD;
}
}
/**
* The shared static process manager, that synchronizes all communication
* with the remote process.
*/
static class WolfProcess extends Thread {
private Process process;
private BufferedReader reader;
private PrintWriter writer;
private ExecutorService executor;
private boolean running;
public boolean getRunning() {
return running;
}
public WolfProcess() {
process = null;
reader = null;
writer = null;
running = true;
executor = Executors.newFixedThreadPool(1);
}
public void endProcess() {
running = false;
}
/**
* WolfProcess thread body. Keeps the remote connection alive.
*/
public void run() {
try {
System.out.println("Starting Wolf<custom-name> remote process");
ProcessBuilder pb = new ProcessBuilder("<invocation>".split(" "));
pb.redirectErrorStream(true);
process = pb.start();
System.out.println("Wolf<custom-name> process begun");
// STDOUT of the process.
reader = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
System.out.println("Wolf<custom-name> reader stream grabbed");
// STDIN of the process.
writer = new PrintWriter(new OutputStreamWriter(process.getOutputStream(), "UTF-8"));
System.out.println("Wolf<custom-name> writer stream grabbed");
while(running){
this.sleep(0);
}
reader.close();
writer.close();
process.destroy(); // kill it with fire.
executor.shutdownNow();
} catch (Exception e) {
e.printStackTrace();
System.out.println("Wolf<custom-name> ended catastrophically.");
}
}
/**
* Helper that invokes a read with a timeout
*/
private String getReply(long timeout) throws TimeoutException, ExecutionException, InterruptedException{
Callable<String> readTask = new Callable<String>() {
@Override
public String call() throws Exception {
return reader.readLine();
}
};
Future<String> future = executor.submit(readTask);
return future.get(timeout, TimeUnit.MILLISECONDS);
}
/**
* Sends an initialization command to the remote process
*/
public synchronized boolean initWolf(int wolf, int map_sz) {
while(writer == null){
try {
this.sleep(0);
}catch(Exception e){}
}
boolean success = false;
try{
writer.printf("S%02d%d\n", wolf, map_sz);
writer.flush();
String reply = getReply(5000l);
if (reply != null && reply.length() >= 3 && reply.charAt(0) == 'K') {
int id = Integer.valueOf(reply.substring(1));
if (wolf == id) {
success = true;
}
}
if (reply == null) {
System.out.println("did not get reply");
}
} catch (TimeoutException ie) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to initialize, timeout\n", wolf);
} catch (Exception e) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to initialize, %s\n", wolf, e.getMessage());
}
return success;
}
/**
* Send an ATTACK command to the remote process.
*/
public synchronized Attack fight(int wolf, char opponent) {
Attack atk = Attack.SUICIDE;
try{
writer.printf("A%02d%c\n", wolf, opponent);
writer.flush();
String reply = getReply(1000l);
if (reply.length() >= 3) {
int id = Integer.valueOf(reply.substring(1));
if (wolf == id) {
switch(reply.charAt(0)) {
case 'R':
atk = Attack.ROCK;
break;
case 'P':
atk = Attack.PAPER;
break;
case 'S':
atk = Attack.SCISSORS;
break;
case 'D':
atk = Attack.SUICIDE;
break;
}
}
}
} catch (TimeoutException ie) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to attack, timeout\n", wolf);
} catch (Exception e) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to attack, %s\n", wolf, e.getMessage());
}
return atk;
}
/**
* Send a MOVE command to the remote process.
*/
public synchronized Move move(int wolf, char[][] map) {
Move move = Move.HOLD;
try{
writer.printf("M%02d", wolf);
for (int row=0; row<map.length; row++) {
for (int col=0; col<map[row].length; col++) {
writer.printf("%c", map[row][col]);
}
}
writer.print("\n");
writer.flush();
String reply = getReply(1000l);
if (reply.length() >= 3) {
int id = Integer.valueOf(reply.substring(1));
if (wolf == id) {
switch(reply.charAt(0)) {
case 'H':
move = Move.HOLD;
break;
case 'U':
move = Move.UP;
break;
case 'L':
move = Move.LEFT;
break;
case 'R':
move = Move.RIGHT;
break;
case 'D':
move = Move.DOWN;
break;
}
}
}
} catch (TimeoutException ie) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to move, timeout\n", wolf);
} catch (Exception e) {
endProcess();
System.out.printf("Wolf<custom-name> %d failed to move, %s\n", wolf, e.getMessage());
}
return move;
}
}
}