44import org .json .*;
55
66import java .io .IOException ;
7+ import java .io .OutputStream ;
78import java .nio .file .*;
89
910import battlecode .common .*;
11+ import battlecode .instrumenter .stream .RoboPrintStream ;
1012
1113/**
1214 * Allows bots written in different languages to be run by the Java engine using a message-passing system.
@@ -21,15 +23,26 @@ public class CrossPlay {
2123 LOCK_FILE_JAVA = "lock_java.txt" , // lock file created by the java engine
2224 LOCK_FILE_OTHER = "lock_other.txt" ; // lock file created by the other language's runner script
2325
26+ private final boolean finalizer ;
27+ private boolean initialized ;
2428 private ArrayList <Object > objects ;
2529 private RobotController rc ;
30+ private CrossPlayReference rcRef ;
2631 private int roundNum ;
2732 private Team team ;
2833 private int id ;
29- private CrossPlayReference rcRef ;
34+ private OutputStream out ;
3035
3136 public CrossPlay () {
3237 this .objects = new ArrayList <>();
38+ this .finalizer = false ;
39+ this .initialized = false ;
40+ }
41+
42+ public CrossPlay (boolean finalizer ) {
43+ this .objects = new ArrayList <>();
44+ this .finalizer = finalizer ;
45+ this .initialized = false ;
3346 }
3447
3548 private void clearObjects () {
@@ -116,7 +129,7 @@ private CrossPlayReference setNextObject(CrossPlayObjectType type, Object value)
116129 return ref ;
117130 }
118131
119- public void runMessagePassing () {
132+ public int runMessagePassing () throws GameActionException {
120133 Path crossPlayDir = Paths .get (CROSS_PLAY_DIR );
121134 Path javaMessagePath = crossPlayDir .resolve (MESSAGE_FILE_JAVA );
122135 Path otherMessagePath = crossPlayDir .resolve (MESSAGE_FILE_OTHER );
@@ -127,7 +140,7 @@ public void runMessagePassing() {
127140 while (true ) {
128141 try {
129142 if (!Files .exists (otherMessagePath ) || Files .exists (javaMessagePath ) || Files .exists (otherLockPath )) {
130- Thread .sleep (100 , 0 ); // TODO make shorter: sleep for 0.1 s
143+ Thread .sleep (0 , 100000 ); // TODO currently 0.1 ms, maybe make shorter
131144 // System.out.println("Still waiting for message Python -> Java...");
132145 continue ;
133146 }
@@ -144,20 +157,22 @@ public void runMessagePassing() {
144157
145158 // System.out.println("Received message Python -> Java: " + messageJson.toString());
146159
147- if (message .method == CrossPlayMethod .TERMINATE ) {
148- Files .delete (otherMessagePath );
149- Files .delete (javaLockPath );
150- // System.out.println("Received terminate message, ending cross-play message passing.");
151- break ;
152- }
153-
154160 CrossPlayObject result = processMessage (message );
155161 String resultContent = result .toJson ().toString ();
156162 Files .writeString (javaMessagePath , resultContent );
157163
158164 Files .delete (otherMessagePath );
159165 Files .delete (javaLockPath );
160166
167+ if (message .method == CrossPlayMethod .END_TURN ) {
168+ // System.out.println("Received terminate message, ending cross-play message passing.");
169+ int bytecodeUsed = getLiteralValue (message .params [0 ]);
170+ return bytecodeUsed ;
171+ } else if (this .finalizer && message .method == CrossPlayMethod .START_TURN ) {
172+ // System.out.println("Finalizer received start turn message, ending cross-play message passing.");
173+ return 0 ;
174+ }
175+
161176 // System.out.println("Sent response Java -> Python: " + resultContent);
162177 // System.out.println("Waiting for message Python -> Java...");
163178 } catch (InterruptedException e ) {
@@ -168,71 +183,61 @@ public void runMessagePassing() {
168183 }
169184 }
170185
171- // private JSONArray processJsonMessages(JSONArray json) {
172- // System.out.println("Starting cross-play message processing...");
173- // int length = json.length();
174- // JSONObject[] resultsArr = new JSONObject[length];
175-
176- // for (int i = 0; i < length; i++) {
177- // JSONObject messageJson = json.getJSONObject(i);
178- // CrossPlayMessage message = CrossPlayMessage.fromJson(messageJson);
179- // CrossPlayObject result = processMessage(message);
180- // JSONObject resultJson = result.toJson();
181- // resultsArr[i] = resultJson;
182- // }
183-
184- // JSONArray resultsJson = new JSONArray(resultsArr);
185- // System.out.println("Finished cross-play message processing.");
186- // return resultsJson;
187- // }
188-
189186 private CrossPlayObject processMessage (CrossPlayMessage message ) {
190- CrossPlayObject [] computedParams = new CrossPlayObject [message .params .length ];
191-
192- for (int i = 0 ; i < message .params .length ; i ++) {
193- CrossPlayObject param = message .params [i ];
194-
195- if (param instanceof CrossPlayMessage mess ) {
196- CrossPlayObject innerResult = processMessage (mess );
197- computedParams [i ] = getObject (innerResult );
198- } else {
199- computedParams [i ] = param ;
200- }
201- }
202-
203187 CrossPlayObject result ;
204188 RobotController rc ;
205189
206190 // TODO add cases for all methods
207191 switch (message .method ) {
208192 case INVALID :
209193 throw new CrossPlayException ("Received invalid cross-play method!" );
210- case TERMINATE :
211- throw new CrossPlayException ("Terminate messages should be handled outside of processMessage." );
212194 case START_TURN :
213- result = new CrossPlayLiteral (CrossPlayObjectType .ARRAY , new CrossPlayObject [] {
214- this .rcRef ,
215- new CrossPlayLiteral (CrossPlayObjectType .INTEGER , this .roundNum ),
216- new CrossPlayLiteral (CrossPlayObjectType .TEAM , this .team ),
217- new CrossPlayLiteral (CrossPlayObjectType .INTEGER , this .id ),
218- new CrossPlayLiteral (CrossPlayObjectType .BOOLEAN , false ),
219- });
195+ // System.out.println("Processing START_TURN");
196+ if (this .finalizer ) {
197+ result = new CrossPlayLiteral (CrossPlayObjectType .ARRAY , new CrossPlayObject [] {
198+ CrossPlayLiteral .NULL ,
199+ CrossPlayLiteral .NULL ,
200+ CrossPlayLiteral .NULL ,
201+ CrossPlayLiteral .NULL ,
202+ CrossPlayLiteral .TRUE
203+ });
204+ } else {
205+ result = new CrossPlayLiteral (CrossPlayObjectType .ARRAY , new CrossPlayObject [] {
206+ this .rcRef ,
207+ CrossPlayLiteral .ofInt (this .roundNum ),
208+ CrossPlayLiteral .ofTeam (this .team ),
209+ CrossPlayLiteral .ofInt (this .id ),
210+ CrossPlayLiteral .FALSE
211+ });
212+ }
213+ break ;
214+ case END_TURN :
215+ // System.out.println("Processing END_TURN");
216+ result = new CrossPlayLiteral (CrossPlayObjectType .NULL , null );
220217 break ;
221218 case RC_GET_ROUND_NUM :
222- rc = this .<RobotController >getObject (computedParams [0 ]);
219+ // System.out.println("Processing RC_GET_ROUND_NUM");
220+ rc = this .<RobotController >getObject (message .params [0 ]);
223221 result = new CrossPlayLiteral (CrossPlayObjectType .INTEGER , rc .getRoundNum ());
224222 break ;
225223 case RC_GET_MAP_WIDTH :
226- rc = this .<RobotController >getObject (computedParams [0 ]);
224+ // System.out.println("Processing RC_GET_MAP_WIDTH");
225+ rc = this .<RobotController >getObject (message .params [0 ]);
227226 result = new CrossPlayLiteral (CrossPlayObjectType .INTEGER , rc .getMapWidth ());
228227 break ;
229228 case RC_GET_MAP_HEIGHT :
230- rc = this .<RobotController >getObject (computedParams [0 ]);
229+ // System.out.println("Processing RC_GET_MAP_HEIGHT");
230+ rc = this .<RobotController >getObject (message .params [0 ]);
231231 result = new CrossPlayLiteral (CrossPlayObjectType .INTEGER , rc .getMapHeight ());
232232 break ;
233233 case LOG :
234- String msg = getLiteralValue (computedParams [0 ]);
235- System .out .println (msg );
234+ // System.out.println("Processing LOG");
235+ String msg = getLiteralValue (message .params [0 ]);
236+
237+ if (this .out instanceof RoboPrintStream rps ) {
238+ rps .println (msg );
239+ }
240+
236241 result = new CrossPlayLiteral (CrossPlayObjectType .NULL , null );
237242 break ;
238243 default :
@@ -242,20 +247,32 @@ private CrossPlayObject processMessage(CrossPlayMessage message) {
242247 return result ;
243248 }
244249
245- public void playTurn (RobotController rc ) {
250+ public int playTurn (RobotController rc , OutputStream systemOut ) throws GameActionException {
251+ // System.out.println("playTurn called for CrossPlay instance " + this.hashCode());
252+
253+ if (this .rc == rc && this .roundNum == rc .getRoundNum ()) {
254+ // System.out.println("playTurn returned early for CrossPlay instance " + this.hashCode());
255+ return 0 ;
256+ }
257+
246258 this .rc = rc ;
247259 this .roundNum = rc .getRoundNum ();
248260 this .team = rc .getTeam ();
249261 this .id = rc .getID ();
262+ this .out = systemOut ;
250263
251- if (this .roundNum == 1 ) {
264+ if (this .initialized ) {
265+ this .rcRef = new CrossPlayReference (CrossPlayObjectType .ROBOT_CONTROLLER , 0 );
266+ } else {
252267 clearObjects ();
253268 this .rcRef = setNextObject (CrossPlayObjectType .ROBOT_CONTROLLER , this .rc );
254- System .out .println ("Cross-play bot initialized!" );
255- } else {
256- this .rcRef = new CrossPlayReference (CrossPlayObjectType .ROBOT_CONTROLLER , 0 );
269+ this .initialized = true ;
270+ // System.out.println("Cross-play bot initialized!");
257271 }
258-
259- runMessagePassing ();
272+
273+ // System.out.println("Running message passing for CrossPlay instance " + this.hashCode());
274+ int bytecodeUsed = runMessagePassing ();
275+ // System.out.println("playTurn finished for CrossPlay instance " + this.hashCode());
276+ return bytecodeUsed ;
260277 }
261278}
0 commit comments