diff --git a/examples/hello-gl/hello-gl-standalone.py b/examples/hello-gl/hello-gl-standalone.py new file mode 100755 index 0000000..20bc949 --- /dev/null +++ b/examples/hello-gl/hello-gl-standalone.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +from netorcai.agent import * +from netorcai.standalone import run_agent + +def game_logic(agent): + print("Logging in as a game logic...", end=' ', flush=True) + agent.send_login("py-gl", "game logic") + agent.read_login_ack() + print("done") + + print("Waiting for DO_INIT...", end=' ', flush=True) + do_init = agent.read_do_init() + print("done") + + print("Sending DO_INIT_ACK...", end=' ', flush=True) + agent.send_do_init_ack({"all_clients":{"gl": "python"}}) + print("done") + + for i in range(do_init.nb_turns_max): + print("Waiting for DO_TURN...", end=' ', flush=True) + do_turn = agent.read_do_turn() + print("done") + + print("Sending DO_TURN_ACK...", end=' ', flush=True) + agent.send_do_turn_ack({"all_clients":{"gl": "python"}}, -1) + print("done") + +if __name__ == '__main__': + run_agent(game_logic) diff --git a/examples/hello-gl/hello-gl.py b/examples/hello-gl/hello-gl.py index 39c0291..6abee5b 100755 --- a/examples/hello-gl/hello-gl.py +++ b/examples/hello-gl/hello-gl.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from netorcai.client import * +from netorcai.agent import * import sys def main(): try: - client = Client() + client = Agent() print("Connecting to netorcai...", end=' ', flush=True) client.connect() diff --git a/examples/hello-player/hello-player-standalone.py b/examples/hello-player/hello-player-standalone.py new file mode 100644 index 0000000..4021e12 --- /dev/null +++ b/examples/hello-player/hello-player-standalone.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +from netorcai.agent import * +import sys +from netorcai.standalone import run_agent + +def play(agent): + + print("Connecting to netorcai...", end=' ', flush=True) + agent.connect() + print("done") + + print("Logging in as a player...", end=' ', flush=True) + agent.send_login("py-player", "player") + agent.read_login_ack() + print("done") + + print("Waiting for GAME_STARTS...", end=' ', flush=True) + game_starts = agent.read_game_starts() + print("done") + + for i in range(game_starts.nb_turns_max): + print("Waiting for TURN...", end=' ', flush=True) + turn = agent.read_turn() + print("done") + + actions = [{"player": "python"}] + print("Sending actions {}...".format(actions), end=' ', flush=True) + agent.send_turn_ack(turn.turn_number, actions) + print("done") + +if __name__ == '__main__': + run_agent(game_logic) diff --git a/examples/hello-player/hello-player.py b/examples/hello-player/hello-player.py index bd66e1e..b0d3ec4 100755 --- a/examples/hello-player/hello-player.py +++ b/examples/hello-player/hello-player.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from netorcai.client import * +from netorcai.agent import * import sys def main(): try: - client = Client() + client = Agent() print("Connecting to netorcai...", end=' ', flush=True) client.connect() diff --git a/examples/hello-splayer/hello-splayer.py b/examples/hello-splayer/hello-splayer.py index d42f79d..d4e61d5 100755 --- a/examples/hello-splayer/hello-splayer.py +++ b/examples/hello-splayer/hello-splayer.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from netorcai.client import * +from netorcai.agent import * import sys def main(): try: - client = Client() + client = Agent() print("Connecting to netorcai...", end=' ', flush=True) client.connect() diff --git a/examples/hello-visu/hello-visu-standalone.py b/examples/hello-visu/hello-visu-standalone.py new file mode 100644 index 0000000..cff13a0 --- /dev/null +++ b/examples/hello-visu/hello-visu-standalone.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +from netorcai.agent import * +from netorcai.standalone import run_agent + +def visualize(agent): + + client = Agent() + + print("Connecting to netorcai...", end=' ', flush=True) + client.connect() + print("done") + + print("Logging in as a visualization...", end=' ', flush=True) + client.send_login("py-visu", "visualization") + client.read_login_ack() + print("done") + + print("Waiting for GAME_STARTS...", end=' ', flush=True) + game_starts = client.read_game_starts() + print("done") + + for i in range(game_starts.nb_turns_max): + print("Waiting for TURN...", end=' ', flush=True) + turn = client.read_turn() + print("done") + + print("world state for turn", i, turn.game_state) + + actions = [] + print("Sending actions {}...".format(actions), end=' ', flush=True) + client.send_turn_ack(turn.turn_number, actions) + print("done") + +if __name__ == '__main__': + run_agent(visualize) diff --git a/examples/hello-visu/hello-visu.py b/examples/hello-visu/hello-visu.py index c2cd59f..666802c 100755 --- a/examples/hello-visu/hello-visu.py +++ b/examples/hello-visu/hello-visu.py @@ -1,10 +1,10 @@ #!/usr/bin/env python3 -from netorcai.client import * +from netorcai.agent import * import sys def main(): try: - client = Client() + client = Agent() print("Connecting to netorcai...", end=' ', flush=True) client.connect() @@ -24,6 +24,8 @@ def main(): turn = client.read_turn() print("done") + print("world state for turn", i, turn.game_state) + actions = [] print("Sending actions {}...".format(actions), end=' ', flush=True) client.send_turn_ack(turn.turn_number, actions) diff --git a/netorcai/client.py b/netorcai/agent.py similarity index 88% rename from netorcai/client.py rename to netorcai/agent.py index e0881d3..53d3e0f 100644 --- a/netorcai/client.py +++ b/netorcai/agent.py @@ -17,27 +17,40 @@ def recvall(sock, size, flags=0): data += packet return data -class Client: - """A netorcai Client. +class Agent: + """A netorcai Agent. - Handles client-side communications of the netorcai metaprotocol. - Most of the time, only the following methods should be called: - - connect() and close(), to connect to or disconnect from netorcai + An agent is any netorcai program except the gamemaster. + Handles agent-side communications of the netorcai metaprotocol. + + Once connected to the gamemaster, only the following methods + should be called: + - close(), to connect to or disconnect from netorcai - send_, to send a message to netorcai - - recv_, to blockingly receive a message from netorcai + - recv_, to blockingly receive a message from netorcai. + + In order to connect, you can either use: + - connect() to connect to a gamemaster running as a server + - take_over() to use an existing socket, for instance to talk to a + gamemaster connecting to this program as a server. """ def __init__(self): - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) + pass def __del__(self): self.close() def connect(self, hostname="localhost", port=4242): + self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.socket.setsockopt(socket.SOL_TCP, socket.TCP_NODELAY, 1) self.socket.connect((hostname, port)) + def take_over(self, socket): + self.socket = socket + def close(self): - self.socket.close() + if self.socket: + self.socket.close() def send_string(self, string): send_buffer = (string + "\n").encode('utf-8') diff --git a/netorcai/standalone.py b/netorcai/standalone.py new file mode 100644 index 0000000..4f3c4ff --- /dev/null +++ b/netorcai/standalone.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 + +import netorcai.agent +import argparse +import socket +from threading import Thread + +def run_agent(game_func): + parser = argparse.ArgumentParser() + parser.add_argument("--server", help="run as a server", action="store_true") + parser.add_argument("--port", type=int, help="port to use (as server or client)") + parser.add_argument("--host", type=str, help="host to connect to (as a client)", + default="localhost") + parser.add_argument("--verbose") + + args = parser.parse_args() + + if args.server: + serve(args, game_func) + else: + run_client(args, game_func) + +def run_client(args, game_func): + agent = netorcai.agent.Agent() + + port = args.port or 4242 + host = args.host or "localhost" + + if args.verbose: + print("Connecting to netorcai...", end=' ', flush=True) + agent.connect(hostname=host, port=port) + if args.verbose: + print("done") + + try: + game_func(agent) + except Exception as e: + print(e) + sys.exit(1) + sys.exit(0) + + +def serve(args, game_func): + + port = args.port or 4567 + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.bind(('', port)) + sock.listen() + + while True: + (clientsocket, address) = sock.accept() + + def run_on_sock(): + agent = netorcai.agent.Agent() + agent.take_over(clientsocket) + try: + game_func(agent) + except Exception as game_exc: + print("fatal error in game function", game_exc) + + t = Thread(target=run_on_sock) + t.run()