Contenedor para envíos no Java
NOTA Se ha agregado la compatibilidad con MAP_SIZE. Si le importa, actualice su envío en consecuencia.
Esta es una entrada de wiki comunitaria para un contenedor, que pueden usar aquellos que quieran jugar pero no les gusta / no conocen Java. Úselo, diviértase, y estoy feliz de ayudarlo a configurar las cosas.
Es bastante tarde aquí cuando estoy terminando, así que otros codificadores de Java, revisen esto y sugieran mejoras. Si puede, hágalo a través de mi repositorio github presentando un problema o enviando un parche. ¡Gracias!
Todo esto se distribuye con la UNLICENSE, sígalo / bifurque desde su repositorio github . Envíe parches allí si encuentra problemas y actualizaré esta publicación.
Ejemplos actuales de envoltura en uso
plannapus : WolfCollectiveMemory en R
Cómo utilizar
Lo que sigue son instrucciones sobre el protocolo para la comunicación entre procesos a través de TUBOS que he definido para lobos remotos. Tenga en cuenta que he omitido MAP_SIZE ya que esto no parece existir, a pesar de su presencia en la declaración del problema de OP. Si aparece, actualizaré esta publicación.
NOTAS IMPORTANTES :
- Solo se realizará una sola invocación de su proceso externo (por lo tanto, ajuste su lógica de procesamiento en un bucle infinito. Esto también le permite mantener cualquier procesamiento en la memoria, en lugar de usar el disco)
- Toda la comunicación es a este único proceso externo a través de STDIN y STDOUT
- Debe vaciar explícitamente toda la salida enviada a STDOUT y asegurarse de que esté terminada en línea nueva
Especificación
Los scripts remotos son compatibles con un protocolo simple a través de los ganchos STDIN y STDOUT, y se divide en inicialización, movimiento y ataque. En cada caso, la comunicación con su proceso se realizará a través de STDIN, y se necesita una respuesta de STDOUT. Si no se recibe una respuesta en 1 segundo, se supondrá que su proceso está inactivo y se lanzará una excepción. Todos los caracteres se codificarán en UTF-8, por coherencia. Cada entrada terminará con un carácter de nueva línea, y su proceso también debe terminar cada respuesta de salida con una nueva línea.
ADVERTENCIA Asegúrese de vaciar el búfer de salida después de cada escritura, para asegurarse de que el contenedor Java vea su salida. Si no se descarga, su Wolf remoto puede fallar.
Tenga en cuenta que solo se creará un único proceso, todos los Lobos deben administrarse dentro de ese único proceso. Siga leyendo para saber cómo ayudará esta especificación.
Inicialización
STDIN: S<id><mapsize>
\ n
STDOUT: K<id>
\ n
<id>
: 00
o 01
o ... o99
Explicación:
El personaje S
se enviará seguido de dos caracteres numéricos 00
, 01
, ..., 99
indicando cuál de los 100 lobos se está inicializando. En toda comunicación futura con ese lobo específico, <id>
se utilizará lo mismo.
Después de la ID, se enviará una secuencia de longitud variable de caracteres numéricos. Este es el tamaño del mapa. Sabrá que la secuencia de caracteres numéricos termina cuando llegue a la nueva línea ( \n
).
Para asegurarse de que su proceso esté activo, debe responder con el carácter K
seguido del mismo <id>
que recibió. Cualquier otra respuesta dará como resultado una excepción, matando a tus lobos.
Movimiento
STDIN: M<id><C0><C1>...<C7><C8>
\ n
STDOUT: <mv><id>
\ n
<Cn>
: W
o
o B
o S
oL
W
: Lobo
: Espacio vacío
B
: Oso
S
: Piedra
L
: León
<mv>
: H
o U
o L
o R
oD
H
: Move.HOLD
U
: Move.UP
L
: Move.LEFT
R
: Move.RIGHT
D
: Move.DOWN
Explicación:
El personaje M
será enviado seguido de los dos personajes <id>
para indicar qué Wolf necesita elegir un movimiento. A continuación, se enviarán 9 caracteres que representan los alrededores de Wolf, en orden de fila (fila superior, fila central, fila inferior de izquierda a derecha).
Responda con uno de los caracteres de movimiento válidos <mv>
, seguido de los dos dígitos del Lobo <id>
para su confirmación.
Ataque
STDIN: A<id><C>
\ n
STDOUT: <atk><id>
\ n
<C>
: W
o B
o S
oL
<atk>
: R
o P
o S
oD
R
: Attack.ROCK
P
: Attack.PAPER
S
: Ataque . TIJERAS
D
: Ataque . SUICIDIO
Explicación:
El personaje A
será enviado seguido de los dos caracteres <id>
para indicar qué Wolf está participando en un ataque. Esto es seguido por un solo carácter <C>
que indica qué tipo de cosa está atacando, ya sea W
olf, B
ear, S
tone o L
ion.
Responda con uno de los <atk>
caracteres enumerados anteriormente, indicando cuál es su respuesta al ataque, seguido de los dos dígitos <id>
para su confirmación.
Y eso es. No hay más que eso. Si pierdes un ataque, eso <id>
nunca será enviado a tu proceso de nuevo, así es como sabrás que tu Lobo ha muerto, si una ronda de Movimiento completa ha pasado sin que eso <id>
haya sido enviado.
Conclusión
Tenga en cuenta que cualquier excepción matará a todos los Lobos de su tipo remoto, ya que solo un "Proceso" se construye con su lobo remoto, para todos los lobos de su tipo que se crean.
En este repositorio encontrarás el Wolf.java
archivo. Busque y reemplace las siguientes cadenas para configurar su bot:
Reemplácelo <invocation>
con el argumento de la línea de comando que ejecutará correctamente su proceso.
Reemplace <custom-name>
con un nombre único para su lobo.
Para un ejemplo, mire el repositorio , donde tengo WolfRandomPython.java
que invoca mi ejemplo remoto, el PythonWolf.py
(un Python 3+ Wolf).
Cambie el nombre del archivo a ser Wolf<custom-name>.java
, donde <custom-name>
se reemplaza con el nombre que eligió anteriormente.
Para probar su Wolf, compile el programa Java ( javac Wolf<custom-name>.java
) y siga las instrucciones de Rusher para incluirlo en el programa de simulación.
Importante: asegúrese de proporcionar instrucciones claras y concisas sobre cómo compilar / ejecutar su Wolf real, que sigue el esquema que describí anteriormente.
Buena suerte, y que la naturaleza esté siempre a tu favor.
El código de envoltura
Recuerde, DEBE hacer las búsquedas y reemplazos descritos para que esto funcione. Si su invocación es particularmente complicada, comuníquese conmigo para obtener ayuda.
Tenga en cuenta que hay un main
método en este contenedor, para permitir pruebas rudimentarias de "pasar / fallar" en su caja local. Para hacerlo, descargue la clase Animal.java del proyecto y elimine la package animals;
línea de ambos archivos. Reemplace la línea MAP_SIZE en Animal.java con alguna constante (como 100). Compílelos usando javac Wolf<custom-name>.java
un ejecutar vía 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;
}
}
}