Wrapper per invii non Java
NOTA È stato aggiunto il supporto MAP_SIZE. Se ti interessa, ti preghiamo di aggiornare la tua richiesta di conseguenza.
Questa è la voce wiki della community per un wrapper, utilizzabile da coloro che vogliono giocare ma non amano / non conoscono Java. Usalo, divertiti e sono felice di aiutarti a configurare le cose.
È abbastanza tardi qui mentre sto finendo, quindi altri programmatori Java, per favore guarda questo e suggerisci miglioramenti. Se puoi, fallo tramite il mio repository github presentando un problema o inviando una patch. Grazie!
Questo intero viene distribuito con UNLICENSE, segui / fork dal suo repository github . Invia le patch lì se trovi problemi e aggiornerò questo post.
Esempi attuali di wrapper in uso
plannapus : WolfCollectiveMemory in R
Come usare
Quelle che seguono sono le istruzioni sul protocollo per la comunicazione tra processi tramite PIPES che ho definito per i Lupi remoti. Nota: ho saltato MAP_SIZE poiché questo non sembra esistere, nonostante la sua presenza nell'affermazione del problema di OP. Se appare, aggiornerò questo post.
NOTE IMPORTANTI :
- Verrà effettuata una sola invocazione del processo esterno (quindi avvolgere la logica di elaborazione in un ciclo infinito. Ciò consente anche di conservare qualsiasi elaborazione in memoria, anziché utilizzare il disco)
- Tutta la comunicazione avviene con questo singolo processo esterno tramite STDIN e STDOUT
- È necessario svuotare esplicitamente tutto l'output inviato a STDOUT e assicurarsi che sia terminato a capo
specificazione
Gli script remoti sono supportati da un semplice protocollo tramite hook STDIN e STDOUT e sono suddivisi in inizializzazione, Sposta e Attacco. In ogni caso, la comunicazione con il processo avverrà tramite STDIN e sarà necessaria una risposta da STDOUT. Se una risposta non viene ricevuta entro 1 secondo, il processo verrà considerato morto e verrà generata un'eccezione. Tutti i personaggi saranno codificati in UTF-8, per coerenza. Ogni input termina con un carattere di nuova riga e il processo dovrebbe terminare ogni risposta di output anche con una nuova riga.
ATTENZIONE Assicurarsi di svuotare il buffer di output dopo ogni scrittura, per assicurarsi che il wrapper Java visualizzi l'output. Se non si scarica, il Lupo remoto potrebbe non funzionare.
Nota che verrà creato un solo processo, tutti i Lupi devono essere gestiti all'interno di quel processo. Continua a leggere per sapere come queste specifiche ti aiuteranno.
Inizializzazione
STDIN: S<id><mapsize>
\ n
STDOUT: K<id>
\ n
<id>
: 00
o 01
o ... o99
Spiegazione:
Il carattere S
verrà inviato seguito da due caratteri numerici 00
, 01
..., 99
indicando quale dei lupi 100 viene inizializzato. In tutte le comunicazioni future con quel lupo specifico, <id>
verrà utilizzato lo stesso .
Dopo l'ID, verrà inviata una sequenza di caratteri numerici a lunghezza variabile. Questa è la dimensione della mappa. Saprai che la sequenza di caratteri numerici è finita quando raggiungi la nuova riga ( \n
).
Per assicurarti che il processo sia attivo, devi rispondere con il carattere K
seguito dallo stesso che <id>
hai ricevuto. Qualsiasi altra risposta comporterà un'eccezione, uccidendo i tuoi lupi.
Movimento
STDIN: M<id><C0><C1>...<C7><C8>
\ n
STDOUT: <mv><id>
\ n
<Cn>
: W
oppure
oppure B
oppure S
oppureL
W
: Lupo
: Spazio vuoto
B
: Orso
S
: Stone
L
: Lion
<mv>
: H
oppure U
oppure L
oppure R
oppureD
H
: Move.HOLD
U
: Move.UP
L
: Move.LEFT
R
: Move.RIGHT
D
: Move.DOWN
Spiegazione:
Il personaggio M
verrà inviato seguito dai due personaggi <id>
per indicare quale Lupo deve scegliere una mossa. Successivamente, verranno inviati 9 caratteri che rappresentano l'ambiente del Lupo, in ordine di riga (riga superiore, riga centrale, riga inferiore dall'estrema sinistra all'estrema destra).
Rispondi con uno dei caratteri di movimento validi <mv>
, seguito dalle due cifre del Lupo <id>
per conferma.
attacco
STDIN: A<id><C>
\ n
STDOUT: <atk><id>
\ n
<C>
: W
oppure B
oppure S
oppureL
<atk>
: R
oppure P
oppure S
oppureD
R
: Attack.ROCK
P
: Attack.PAPER
S
: Attack.SCISSORS
D
: Attack.SUICIDE
Spiegazione:
Il personaggio A
verrà inviato seguito dai due personaggi <id>
per indicare quale Lupo sta partecipando a un attacco. Questo è seguito da un singolo personaggio <C>
che indica quale tipo di cosa sta attaccando, un W
olf, un B
orecchio, un S
tono o uno L
ione.
Rispondi con uno dei <atk>
personaggi sopra elencati, indicando quale è la tua risposta all'attacco, seguito da due cifre <id>
per la conferma.
E questo è tutto. Non c'è altro. Se perdi un attacco, che <id>
non verrà mai più inviato al tuo processo, è così che saprai che il tuo Lupo è morto - se un round di Movimento completo è passato senza che <id>
mai sia stato inviato.
Conclusione
Nota che qualsiasi eccezione ucciderà tutti i Lupi del tuo tipo remoto, poiché solo un singolo "Processo" è costruito dal tuo lupo remoto, per tutti i lupi del tuo tipo che vengono creati.
In questo repository troverai il Wolf.java
file. Cerca e sostituisci le seguenti stringhe per configurare il tuo bot:
Sostituisci <invocation>
con l'argomento della riga di comando che eseguirà correttamente il tuo processo.
Sostituisci <custom-name>
con un nome univoco per il tuo lupo.
Per un esempio guarda il repository , dove ho WolfRandomPython.java
che invoca il mio esempio remoto, il PythonWolf.py
(un Python 3+ Wolf).
Rinominare il file di essere Wolf<custom-name>.java
, in cui <custom-name>
viene sostituito con il nome scelto in precedenza.
Per testare il tuo Wolf, compila il programma Java ( javac Wolf<custom-name>.java
) e segui le istruzioni di Rusher per includerlo nel programma di simulazione.
Importante: assicurati di fornire istruzioni chiare e concise su come compilare / eseguire il tuo Wolf reale, che segue lo schema che ho descritto sopra.
Buona fortuna e che la natura sia sempre a tuo favore.
Il codice wrapper
Ricorda, DEVI fare le ricerche e le sostituzioni delineate affinché questo funzioni. Se la tua invocazione è particolarmente pelosa, ti prego di contattarmi per assistenza.
Si noti che esiste un main
metodo in questo wrapper per consentire test rudimentali di "passaggio / fallimento" sulla propria casella locale. Per fare ciò, scarica la classe Animal.java dal progetto e rimuovi la package animals;
linea da entrambi i file. Sostituisci la riga MAP_SIZE in Animal.java con delle costanti (come 100). Compilali usando javac Wolf<custom-name>.java
un comando 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;
}
}
}