Wrapper para envios não Java
OBSERVAÇÃO O suporte MAP_SIZE foi adicionado. Se você se importa, atualize seu envio de acordo.
Esta é a entrada do wiki da comunidade para um wrapper, utilizável por quem deseja jogar, mas não gosta / não conhece Java. Por favor, use-o, divirta-se e teremos prazer em ajudá-lo a configurar as coisas.
É muito tarde aqui quando estou terminando, então outros codificadores Java, por favor, examinem isso e sugiram melhorias. Se você puder, faça isso através do meu repositório do github registrando um problema ou enviando um patch. Obrigado!
Todo este está sendo distribuído com o UNLICENSE, siga / bifurque-o em seu repositório github . Envie correções lá se você encontrar problemas e eu atualizarei esta postagem.
Exemplos atuais de invólucro em uso
plannapus : WolfCollectiveMemory em R
Como usar
A seguir, são apresentadas instruções sobre o protocolo de comunicação entre processos via PIPES que defini para Wolves remotos. Observe que pulei MAP_SIZE, pois isso parece não existir, apesar de sua presença na declaração do problema do OP. Se aparecer, eu atualizarei esta postagem.
NOTAS IMPORTANTES :
- Somente uma única invocação do seu processo externo será feita (envolva sua lógica de processamento em um loop infinito. Isso também permite manter qualquer processamento na memória, em vez de usar o disco)
- Toda a comunicação é para esse único processo externo via STDIN e STDOUT
- Você deve liberar explicitamente toda a saída enviada para STDOUT e garantir que ela seja finalizada com nova linha
Especificação
Os scripts remotos são suportados por um protocolo simples via ganchos STDIN e STDOUT e são divididos em inicialização, Mover e Ataque. Em cada caso, a comunicação com seu processo será via STDIN, e é necessária uma resposta do STDOUT. Se uma resposta não for recebida em 1 segundo, seu processo será considerado morto e uma exceção será lançada. Todos os caracteres serão codificados em UTF-8, para consistência. Cada entrada será finalizada com um caractere de nova linha, e seu processo deverá finalizar cada resposta de saída com uma nova linha também.
AVISO Certifique-se de limpar seu buffer de saída após cada gravação, para garantir que o wrapper Java veja sua saída. A falta de descarga pode causar falha no seu Lobo remoto.
Observe que apenas um único processo será criado, todos os lobos devem ser gerenciados dentro desse processo. Leia sobre como essa especificação ajudará.
Inicialização
STDIN: S<id><mapsize>
\ n
STDOUT: K<id>
\ n
<id>
: 00
ou 01
ou ... ou99
Explicação:
O personagem S
será enviado seguido por dois caracteres numéricos 00
, 01
, ..., 99
indicando que dos 100 lobos está sendo inicializado. Em toda a comunicação futura com esse lobo específico, o mesmo <id>
será usado.
Após o ID, uma sequência de comprimento variável de caracteres numéricos será enviada. Este é o tamanho do mapa. Você saberá que a sequência de caracteres numéricos acabou quando você alcança a nova linha ( \n
).
Para garantir que seu processo esteja ativo, você deve responder com o personagem K
seguido pelo mesmo <id>
que recebeu. Qualquer outra resposta resultará em uma exceção, matando seus lobos.
Movimento
STDIN: M<id><C0><C1>...<C7><C8>
\ n
STDOUT: <mv><id>
\ n
<Cn>
: W
ou
ou B
ou S
ouL
W
: Wolf
: Espaço Vazio
B
: Urso
S
: Pedra
L
: Leão
<mv>
: H
ou U
ou L
ou R
ouD
H
: Move.HOLD
U
: Move.UP
L
: Move.LEFT
R
: Move.RIGHT
D
: Move.DOWN
Explicação:
O personagem M
será enviado seguido pelos dois caracteres <id>
para indicar qual Lobo precisa escolher uma jogada. Em seguida, serão enviados 9 caracteres representando o ambiente de Wolf, em ordem de linha (linha superior, linha do meio, linha inferior da esquerda para a direita).
Responda com um dos caracteres de movimento válidos <mv>
, seguido pelos dois dígitos do Lobo <id>
para confirmação.
Ataque
STDIN: A<id><C>
\ n
STDOUT: <atk><id>
\ n
<C>
: W
ou B
ou S
ouL
<atk>
: R
ou P
ou S
ouD
R
: Attack.ROCK
P
: Attack.PAPER
S
: Attack.SCISSORS
D
: Attack.SUICIDE
Explicação:
O personagem A
será enviado seguido pelos dois <id>
para indicar qual Wolf está participando de um ataque. Isso é seguido por um único caractere <C>
indicando que tipo de coisa está atacando, um W
olf, B
ouvido, S
tom ou L
íon.
Responda com um dos <atk>
caracteres listados acima, indicando qual é a sua resposta ao ataque, seguida pelos dois dígitos <id>
para confirmação.
E é isso. Não há mais nada. Se você perder um ataque, isso <id>
nunca será enviado ao seu processo novamente, é assim que você saberá que seu Lobo morreu - se uma rodada completa de Movimento tiver passado sem que ela <id>
nunca tenha sido enviada.
Conclusão
Observe que quaisquer exceções matam todos os lobos do seu tipo remoto, pois apenas um "Processo" é construído do seu lobo remoto, para todos os lobos do seu tipo que são criados.
Neste repositório, você encontrará o Wolf.java
arquivo. Pesquise e substitua as seguintes strings para configurar seu bot:
Substitua <invocation>
pelo argumento da linha de comandos que executará corretamente seu processo.
Substitua <custom-name>
por um nome exclusivo para o seu lobo.
Para um exemplo, veja o repositório , onde eu tenho o WolfRandomPython.java
que chama meu exemplo remoto, o PythonWolf.py
(um Python 3+ Wolf).
Renomeie o arquivo Wolf<custom-name>.java
, onde <custom-name>
é substituído pelo nome que você escolheu acima.
Para testar seu Wolf, compile o programa Java ( javac Wolf<custom-name>.java
) e siga as instruções de Rusher para incluí-lo no programa de simulação.
Importante: Certifique-se de fornecer instruções claras e concisas sobre como compilar / executar seu Wolf real, que segue o esquema que descrevi acima.
Boa sorte e que a natureza esteja sempre a seu favor.
O Código do Wrapper
Lembre-se, você DEVE fazer as pesquisas e substituições descritas para que isso funcione. Se a sua chamada for particularmente complicada, entre em contato comigo para obter assistência.
Observe que há um main
método neste wrapper para permitir testes rudimentares de "aprovação / reprovação" na caixa local. Para fazer isso, baixe a classe Animal.java do projeto e remova a package animals;
linha dos dois arquivos. Substitua a linha MAP_SIZE em Animal.java por alguma constante (como 100). Compile-os usando javac Wolf<custom-name>.java
um 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;
}
}
}