Opakowanie dla zgłoszeń innych niż Java
UWAGA Dodano obsługę MAP_SIZE. Jeśli Cię to obchodzi, zaktualizuj odpowiednio swoje zgłoszenie.
To jest wpis wiki społeczności dla opakowania, do użytku przez tych, którzy chcą grać, ale nie lubią / nie znają Java. Skorzystaj z niego, baw się dobrze i cieszę się, że pomogę Ci to wszystko skonfigurować.
Jest już dość późno, kiedy kończę, więc inni koderzy Java, przejrzyjcie to i zasugerujcie ulepszenia. Jeśli możesz, zrób to za pośrednictwem mojego repozytorium github, zgłaszając problem lub przesyłając łatkę. Dzięki!
Całość jest rozpowszechniana wraz z UNLICENSE, proszę śledź / rozwidlaj ją z repozytorium github . Prześlij łaty, jeśli znajdziesz problemy, a ja zaktualizuję ten post.
Aktualne przykłady zastosowania owijarki
plannapus : WolfCollectiveMemory in R
szczoteczka do zębów : Szczoteczka do zębów w ECMAScript
Jak używać
Poniżej znajdują się instrukcje dotyczące protokołu komunikacji między procesami za pośrednictwem PIPES, które zdefiniowałem dla zdalnych wilków. Uwaga Pominąłem MAP_SIZE, ponieważ wydaje się, że nie istnieje, pomimo jego obecności w zgłoszeniu problemu OP. Jeśli się pojawi, zaktualizuję ten post.
WAŻNE UWAGI :
- Zostanie wykonane tylko jedno wywołanie twojego procesu zewnętrznego (więc zawiń swoją logikę przetwarzania w nieskończoną pętlę. Pozwala to również zachować dowolne przetwarzanie w pamięci, zamiast używania dysku)
- Cała komunikacja odbywa się do tego jednego zewnętrznego procesu poprzez STDIN i STDOUT
- Musisz jawnie opróżnić wszystkie dane wyjściowe wysłane do STDOUT i upewnić się, że są zakończone znakiem nowej linii
Specyfikacja
Skrypty zdalne są obsługiwane przez prosty protokół za pośrednictwem haków STDIN i STDOUT i są podzielone na inicjalizację, ruch i atak. W każdym przypadku komunikacja z twoim procesem będzie odbywać się za pośrednictwem STDIN i konieczna jest odpowiedź STDOUT. Jeśli odpowiedź nie zostanie odebrana w ciągu 1 sekundy, proces zostanie uznany za martwy i zostanie zgłoszony wyjątek. Wszystkie znaki będą kodowane w UTF-8, dla zachowania spójności. Każde wejście zakończy się znakiem nowej linii, a twój proces powinien również zakończyć każdą odpowiedź wyjściową znakiem nowej linii.
OSTRZEŻENIE Pamiętaj, aby opróżnić bufor wyjściowy po każdym zapisie, aby upewnić się, że opakowanie Java widzi dane wyjściowe. Brak spłukiwania może spowodować awarię twojego zdalnego Wilka.
Pamiętaj, że zostanie utworzony tylko jeden proces, wszystkie Wilki muszą być zarządzane w ramach tego jednego procesu. Czytaj dalej, aby dowiedzieć się, jak ta specyfikacja pomoże.
Inicjalizacja
STDIN: S<id><mapsize>
\ n
STDOUT: K<id>
\ n
<id>
: 00
lub 01
lub ... lub99
Wyjaśnienie:
Postać S
zostanie wysłany następnie dwóch cyfr 00
, 01
, ..., 99
wskazując, które z 100 wilków jest zainicjowany. W całej przyszłej komunikacji z tym konkretnym wilkiem <id>
będzie to samo .
Po identyfikatorze zostanie wysłana sekwencja znaków numerycznych o zmiennej długości. To jest rozmiar mapy. Przekonasz się, że sekwencja znaków numerycznych dobiegła końca, gdy dojdziesz do nowej linii ( \n
).
Aby upewnić się, że proces jest aktywny, musisz odpowiedzieć postacią, K
a następnie tą samą, <id>
którą otrzymałeś. Każda inna odpowiedź spowoduje wyjątek, zabijając twoje wilki.
Ruch
STDIN: M<id><C0><C1>...<C7><C8>
\ n
STDOUT: <mv><id>
\ n
<Cn>
: W
lub
lub B
lub S
lubL
W
: Wolf
: Pusta przestrzeń
B
: Niedźwiedź
S
: Kamień
L
: Lew
<mv>
: H
lub U
lub L
lub R
lubD
H
: Move.HOLD
U
: Move.UP
L
: Move.LEFT
R
: Move.RIGHT
D
: Move.DOWN
Wyjaśnienie:
Postać M
zostanie wysłana, a następnie dwie postaci, <id>
aby wskazać, który Wilk musi wybrać ruch. Następnie zostanie wysłanych 9 znaków reprezentujących otoczenie Wilka, w kolejności rzędów (górny rząd, środkowy rząd, dolny rząd od lewej do prawej).
Odpowiedz jednym z prawidłowych znaków ruchu <mv>
, a następnie dwucyfrową <id>
czcionką Wolfa, aby potwierdzić.
Atak
STDIN: A<id><C>
\ n
STDOUT: <atk><id>
\ n
<C>
: W
lub B
lub S
lubL
<atk>
: R
lub P
lub S
lubD
R
: Attack.ROCK
P
: Attack.PAPER
S
: Attack.SCISSORS
D
: Attack.SUICIDE
Wyjaśnienie:
Postać A
zostanie wysłana, a następnie dwie postaci, <id>
aby wskazać, który Wilk uczestniczy w ataku. Po nim następuje pojedynczy znak <C>
wskazujący, jaki rodzaj rzeczy atakuje: W
olf, B
ucho, S
ton lub L
jon.
Odpowiedz jednym z <atk>
wyżej wymienionych znaków, wskazując, jaka jest twoja odpowiedź na atak, a następnie dwucyfrowe <id>
potwierdzenie.
I to wszystko. Nic więcej. Jeśli przegrasz atak, który <id>
nigdy nie zostanie wysłany do twojego procesu, w ten sposób dowiesz się, że twój Wilk umarł - jeśli minie pełna runda Ruchu, która <id>
nigdy nie zostanie wysłana.
Wniosek
Zauważ, że wszelkie wyjątki zabiją wszystkie Wilki twojego zdalnego typu, ponieważ tylko jeden „Proces” jest zbudowany z twojego zdalnego wilka, dla wszystkich twoich wilków, które zostaną utworzone.
W tym repozytorium znajdziesz Wolf.java
plik. Wyszukaj i zamień następujące ciągi, aby skonfigurować bota:
Zastąp <invocation>
argumentem wiersza poleceń, który poprawnie wykona twój proces.
Zastąp <custom-name>
unikalną nazwą swojego wilka.
Na przykład spójrz na repozytorium , gdzie mam, WolfRandomPython.java
że wywołuje mój przykład zdalny, PythonWolf.py
(Wilk Python 3+).
Zmień nazwę pliku na Wolf<custom-name>.java
, gdzie <custom-name>
zostanie zastąpiony nazwą, którą wybrałeś powyżej.
Aby przetestować Wolfa, skompiluj program Java ( javac Wolf<custom-name>.java
) i postępuj zgodnie z instrukcjami Rushera, aby włączyć go do programu symulacyjnego.
Ważne: pamiętaj, aby podać jasne , zwięzłe instrukcje dotyczące kompilowania / wykonywania twojego Wolfa, zgodnie ze schematem, który opisałem powyżej.
Powodzenia i niech natura będzie zawsze na twoją korzyść.
Kod opakowania
Pamiętaj, że MUSISZ przeszukiwać i zamieniać opisane w tym celu, aby to zadziałało. Jeśli twoje wezwanie jest szczególnie owłosione, proszę o kontakt w celu uzyskania pomocy.
Zauważ, że main
w tym opakowaniu jest metoda pozwalająca na podstawowe testy „pozytywnego / negatywnego” na twoim lokalnym urządzeniu. Aby to zrobić, pobierz klasę Animal.java z projektu i usuń package animals;
wiersz z obu plików. Zamień wiersz MAP_SIZE w Animal.java na pewną stałą (np. 100). Skompiluj je za javac Wolf<custom-name>.java
pomocą polecenia exec 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;
}
}
}