Skip to content

Commit 3409466

Browse files
committed
fix bugs relating to the order of execution of the python and java processes
1 parent b1a5f90 commit 3409466

3 files changed

Lines changed: 156 additions & 65 deletions

File tree

engine/src/crossplay_python/crossplay.py

Lines changed: 111 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
MESSAGE_FILE_OTHER = "messages_other.json"
1414
LOCK_FILE_JAVA = "lock_java.txt"
1515
LOCK_FILE_OTHER = "lock_other.txt"
16+
STARTED_FILE_JAVA = "started_java.txt"
17+
STARTED_FILE_OTHER = "started_other.txt"
1618

1719
class CrossPlayException(Exception):
1820
def __init__(self, message):
@@ -200,60 +202,130 @@ def from_json(cls, json_data):
200202

201203
# 0.1 ms timestep, 10 min timeout
202204
def wait(message: CrossPlayMessage, timeout=600, timestep=0.0001, message_dir=MESSAGE_DIR):
203-
read_file = os.path.join(message_dir, MESSAGE_FILE_JAVA)
204-
write_file = os.path.join(message_dir, MESSAGE_FILE_OTHER)
205-
java_lock_file = os.path.join(message_dir, LOCK_FILE_JAVA)
206-
other_lock_file = os.path.join(message_dir, LOCK_FILE_OTHER)
205+
try:
206+
read_file = os.path.join(message_dir, MESSAGE_FILE_JAVA)
207+
write_file = os.path.join(message_dir, MESSAGE_FILE_OTHER)
208+
java_lock_file = os.path.join(message_dir, LOCK_FILE_JAVA)
209+
other_lock_file = os.path.join(message_dir, LOCK_FILE_OTHER)
207210

208-
json_message = message.to_json()
209-
time_limit = time.time() + timeout
211+
# if directory does not exist, create it
212+
if not os.path.exists(message_dir):
213+
os.makedirs(message_dir)
210214

211-
# print(f"Waiting to send message Python -> Java: {json_message}")
215+
json_message = message.to_json()
216+
time_limit = time.time() + timeout
212217

213-
while os.path.exists(read_file) or os.path.exists(write_file) or os.path.exists(java_lock_file):
214-
time.sleep(timestep)
218+
# print(f"Waiting to send message Python -> Java: {json_message}")
215219

216-
if time.time() > time_limit:
217-
raise CrossPlayException("Cross-play message passing timed out (Python waiting, Java busy).")
220+
while os.path.exists(read_file) or os.path.exists(write_file) or os.path.exists(java_lock_file):
221+
time.sleep(timestep)
218222

219-
if not os.path.exists(other_lock_file):
220-
with open(other_lock_file, 'x') as f:
221-
f.write('')
223+
if time.time() > time_limit:
224+
raise CrossPlayException("Cross-play message passing timed out (Python waiting, Java busy).")
222225

223-
# print("Created other lock file")
226+
if not os.path.exists(other_lock_file):
227+
with open(other_lock_file, 'x') as f:
228+
f.write('')
224229

225-
with open(write_file, 'w') as f:
226-
json.dump(json_message, f)
230+
# print("Created other lock file")
227231

228-
if os.path.exists(other_lock_file):
229-
os.remove(other_lock_file)
232+
with open(write_file, 'w') as f:
233+
json.dump(json_message, f)
230234

231-
# print(f"Sent message Python -> Java: {json_message}")
232-
# print("Waiting for response Java -> Python...")
233-
time_limit = time.time() + timeout
234-
235-
while not os.path.exists(read_file) or os.path.exists(write_file) or os.path.exists(java_lock_file):
236-
time.sleep(timestep)
235+
if os.path.exists(other_lock_file):
236+
os.remove(other_lock_file)
237+
238+
# print(f"Sent message Python -> Java: {json_message}")
239+
# print("Waiting for response Java -> Python...")
240+
time_limit = time.time() + timeout
241+
242+
while not os.path.exists(read_file) or os.path.exists(write_file) or os.path.exists(java_lock_file):
243+
time.sleep(timestep)
237244

238-
if time.time() > time_limit:
239-
raise CrossPlayException("Cross-play message passing timed out (Python waiting, Java not responding).")
245+
if time.time() > time_limit:
246+
raise CrossPlayException("Cross-play message passing timed out (Python waiting, Java not responding).")
240247

241-
if not os.path.exists(other_lock_file):
242-
with open(other_lock_file, 'x') as f:
243-
f.write('')
248+
if not os.path.exists(other_lock_file):
249+
with open(other_lock_file, 'x') as f:
250+
f.write('')
251+
252+
with open(read_file, 'r') as f:
253+
json_data = json.load(f)
254+
result = CrossPlayObject.from_json(json_data)
255+
256+
os.remove(read_file)
257+
258+
if os.path.exists(other_lock_file):
259+
os.remove(other_lock_file)
244260

245-
with open(read_file, 'r') as f:
246-
json_data = json.load(f)
247-
result = CrossPlayObject.from_json(json_data)
261+
# print(f"Received message Java -> Python: {result}")
262+
263+
if isinstance(result, CrossPlayLiteral):
264+
return result.reduce_literal()
265+
else:
266+
return result
267+
except IOError as e:
268+
raise CrossPlayException("Cross-play message passing failed due to file I/O error: " + str(e))
269+
270+
def reset_files(message_dir=MESSAGE_DIR):
271+
read_file = os.path.join(message_dir, MESSAGE_FILE_JAVA)
272+
write_file = os.path.join(message_dir, MESSAGE_FILE_OTHER)
273+
java_lock_file = os.path.join(message_dir, LOCK_FILE_JAVA)
274+
other_lock_file = os.path.join(message_dir, LOCK_FILE_OTHER)
275+
java_started_file = os.path.join(message_dir, STARTED_FILE_JAVA)
276+
other_started_file = os.path.join(message_dir, STARTED_FILE_OTHER)
277+
278+
if not os.path.exists(message_dir) or not os.path.isdir(message_dir):
279+
os.makedirs(message_dir)
280+
elif os.path.exists(other_started_file):
281+
print("DEBUGGING: Detected existing crossplay_temp/started_other.txt file. " \
282+
"This indicates that a previous cross-play match did not terminate cleanly. " \
283+
"Deleting the old crossplay_temp files.")
284+
elif os.path.exists(java_started_file):
285+
print("DEBUGGING: Java cross-play runner already started. Using existing cross-play temp directory.")
286+
return
287+
288+
if os.path.exists(read_file):
289+
os.remove(read_file)
248290

249-
os.remove(read_file)
291+
if os.path.exists(write_file):
292+
os.remove(write_file)
293+
294+
if os.path.exists(java_lock_file):
295+
os.remove(java_lock_file)
250296

251297
if os.path.exists(other_lock_file):
252298
os.remove(other_lock_file)
299+
300+
if not os.path.exists(os.path.join(message_dir, STARTED_FILE_OTHER)):
301+
with open(other_started_file, 'x') as f:
302+
f.write('')
303+
304+
def clear_temp_files(message_dir=MESSAGE_DIR):
305+
if not os.path.exists(message_dir) or not os.path.isdir(message_dir):
306+
return
253307

254-
# print(f"Received message Java -> Python: {result}")
308+
read_file = os.path.join(message_dir, MESSAGE_FILE_JAVA)
309+
write_file = os.path.join(message_dir, MESSAGE_FILE_OTHER)
310+
java_lock_file = os.path.join(message_dir, LOCK_FILE_JAVA)
311+
other_lock_file = os.path.join(message_dir, LOCK_FILE_OTHER)
312+
java_started_file = os.path.join(message_dir, STARTED_FILE_JAVA)
313+
other_started_file = os.path.join(message_dir, STARTED_FILE_OTHER)
255314

256-
if isinstance(result, CrossPlayLiteral):
257-
return result.reduce_literal()
258-
else:
259-
return result
315+
if os.path.exists(read_file):
316+
os.remove(read_file)
317+
318+
if os.path.exists(write_file):
319+
os.remove(write_file)
320+
321+
if os.path.exists(java_lock_file):
322+
os.remove(java_lock_file)
323+
324+
if os.path.exists(other_lock_file):
325+
os.remove(other_lock_file)
326+
327+
if os.path.exists(java_started_file):
328+
os.remove(java_started_file)
329+
330+
if os.path.exists(other_started_file):
331+
os.remove(other_started_file)

engine/src/crossplay_python/main.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
from crossplay_python.runner import RobotRunner
99
from crossplay_python.crossplay import BYTECODE_LIMIT, CrossPlayLiteral as lit, \
10-
CrossPlayMessage as mess, CrossPlayMethod as m, CrossPlayObjectType as ot, wait
10+
CrossPlayMessage as mess, CrossPlayMethod as m, CrossPlayObjectType as ot, \
11+
wait, reset_files, clear_temp_files
1112
from crossplay_python.wrappers import _GAME_METHODS, Team
1213

1314
CROSSPLAY_PYTHON_DIR = "example-bots/src/crossplay_python"
@@ -49,29 +50,34 @@ def play(team_a=None, team_b=None, debug=False):
4950
Team.B: get_code(team_b),
5051
Team.NEUTRAL: None
5152
}
53+
54+
reset_files()
5255

53-
while True:
54-
rc, round, team, id, end = wait(mess(m.START_TURN, []))
55-
56-
if end:
57-
print("Game ended")
58-
break
59-
60-
if debug:
61-
print(f"Starting turn. Round {round}, team {TEAM_NAMES[team]}, ID {id}, end {end}")
62-
63-
runner = RobotRunner(
64-
code=code[team],
65-
game_methods=_GAME_METHODS,
66-
error_method=get_error_printer(team=team, id=id, round=round),
67-
bytecode_limit=BYTECODE_LIMIT,
68-
debug=debug)
69-
70-
runner.init_robot()
71-
runner.run()
72-
bytecode = runner.bytecode_limit - runner.bytecode
73-
runner.kill()
74-
wait(mess(m.END_TURN, [lit(ot.INTEGER, bytecode)]))
56+
try:
57+
while True:
58+
rc, round, team, id, end = wait(mess(m.START_TURN, []))
59+
60+
if end:
61+
print("Game ended")
62+
break
63+
64+
if debug:
65+
print(f"Starting turn. Round {round}, team {TEAM_NAMES[team]}, ID {id}, end {end}")
66+
67+
runner = RobotRunner(
68+
code=code[team],
69+
game_methods=_GAME_METHODS,
70+
error_method=get_error_printer(team=team, id=id, round=round),
71+
bytecode_limit=BYTECODE_LIMIT,
72+
debug=debug)
73+
74+
runner.init_robot()
75+
runner.run()
76+
bytecode = runner.bytecode_limit - runner.bytecode
77+
runner.kill()
78+
wait(mess(m.END_TURN, [lit(ot.INTEGER, bytecode)]))
79+
finally:
80+
clear_temp_files()
7581

7682
if __name__ == "__main__":
7783
parser = ArgumentParser()

engine/src/main/battlecode/crossplay/CrossPlay.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ public class CrossPlay {
2121
MESSAGE_FILE_JAVA = "messages_java.json", // messages from the java engine
2222
MESSAGE_FILE_OTHER = "messages_other.json", // messages from the other language's runner script
2323
LOCK_FILE_JAVA = "lock_java.txt", // lock file created by the java engine
24-
LOCK_FILE_OTHER = "lock_other.txt"; // lock file created by the other language's runner script
24+
LOCK_FILE_OTHER = "lock_other.txt", // lock file created by the other language's runner script
25+
STARTED_JAVA = "started_java.txt", // file created by the java engine when it starts
26+
STARTED_OTHER = "started_other.txt"; // file created by the other language's runner
2527

2628
private final boolean finalizer;
2729
private boolean initialized;
@@ -55,12 +57,21 @@ public static void resetFiles() {
5557

5658
if (!Files.exists(crossPlayDir) || !Files.isDirectory(crossPlayDir)) {
5759
Files.createDirectory(crossPlayDir);
60+
} else if (Files.exists(crossPlayDir.resolve(STARTED_JAVA))) {
61+
System.out.println("DEBUGGING: Detected existing crossplay_temp/started_java.txt file. "
62+
+ "This indicates that a previous cross-play match did not terminate cleanly. "
63+
+ "Deleting the old crossplay_temp files.");
64+
} else if (Files.exists(crossPlayDir.resolve(STARTED_OTHER))) {
65+
System.out.println("DEBUGGING: Python cross-play runner already started. Using existing cross-play temp directory.");
66+
return;
5867
}
5968

6069
Files.deleteIfExists(crossPlayDir.resolve(MESSAGE_FILE_JAVA));
6170
Files.deleteIfExists(crossPlayDir.resolve(MESSAGE_FILE_OTHER));
6271
Files.deleteIfExists(crossPlayDir.resolve(LOCK_FILE_JAVA));
6372
Files.deleteIfExists(crossPlayDir.resolve(LOCK_FILE_OTHER));
73+
74+
Files.createFile(crossPlayDir.resolve(STARTED_JAVA));
6475
} catch (Exception e) {
6576
throw new CrossPlayException("Failed to clear cross-play lock files.");
6677
}
@@ -75,9 +86,11 @@ public static void clearTempFiles() {
7586
Files.deleteIfExists(crossPlayDir.resolve(MESSAGE_FILE_OTHER));
7687
Files.deleteIfExists(crossPlayDir.resolve(LOCK_FILE_JAVA));
7788
Files.deleteIfExists(crossPlayDir.resolve(LOCK_FILE_OTHER));
89+
Files.deleteIfExists(crossPlayDir.resolve(STARTED_JAVA));
90+
Files.deleteIfExists(crossPlayDir.resolve(STARTED_OTHER));
7891
Files.delete(crossPlayDir);
7992
}
80-
} catch (Exception e) {
93+
} catch (IOException e) {
8194
throw new CrossPlayException("Failed to clear cross-play lock files.");
8295
}
8396
}
@@ -178,7 +191,7 @@ public int runMessagePassing() throws GameActionException {
178191
} catch (InterruptedException e) {
179192
throw new CrossPlayException("Cross-play message passing thread was interrupted.");
180193
} catch (IOException e) {
181-
throw new CrossPlayException("Failed to read other language's cross-play message file.");
194+
throw new CrossPlayException("Cross-play message passing failed due to file I/O error: " + e.getMessage());
182195
}
183196
}
184197
}

0 commit comments

Comments
 (0)