diff options
Diffstat (limited to 'dotsandboxes/test')
| -rw-r--r-- | dotsandboxes/test/cli_compete.py | 212 | ||||
| -rw-r--r-- | dotsandboxes/test/evaluate.py | 240 | 
2 files changed, 452 insertions, 0 deletions
diff --git a/dotsandboxes/test/cli_compete.py b/dotsandboxes/test/cli_compete.py new file mode 100644 index 0000000..ee2aee8 --- /dev/null +++ b/dotsandboxes/test/cli_compete.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +# encoding: utf-8 +""" +dotsandboxescompete.py + +Template for the Machine Learning Project course at KU Leuven (2017-2018) +of Hendrik Blockeel and Wannes Meert. + +Copyright (c) 2018 KU Leuven. All rights reserved. +""" + +import sys +import argparse +import logging +import asyncio +import websockets +import json +from collections import defaultdict +import random +import uuid +import time + +logger = logging.getLogger(__name__) + + +def start_competition(address1, address2, nb_rows, nb_cols, timelimit): +   asyncio.get_event_loop().run_until_complete(connect_agent(address1, address2, nb_rows, nb_cols, timelimit)) + + +async def connect_agent(uri1, uri2, nb_rows, nb_cols, timelimit): +    cur_game = str(uuid.uuid4()) +    winner = None +    cells = [] +    cur_player = 1 +    points = [0, 0, 0] +    timings = [None, [], []] + +    for ri in range(nb_rows + 1): +        columns = [] +        for ci in range(nb_cols + 1): +            columns.append({"v":0, "h":0, "p":0}) +        cells.append(columns) + +    logger.info("Connecting to {}".format(uri1)) +    async with websockets.connect(uri1) as websocket1: +        logger.info("Connecting to {}".format(uri2)) +        async with websockets.connect(uri2) as websocket2: +            logger.info("Connected") + +            # Start game +            msg = { +              "type": "start", +              "player": 1, +              "timelimit": timelimit, +              "game": cur_game, +              "grid": [nb_rows, nb_cols] +            } +            await websocket1.send(json.dumps(msg)) +            msg["player"] = 2 +            await websocket2.send(json.dumps(msg)) + +            # Run game +            while winner is None: +                ask_time = time.time() +                logger.info("Waiting for player {}".format(cur_player)) +                if cur_player == 1: +                    msg = await websocket1.recv() +                else: +                    msg = await websocket2.recv() +                recv_time = time.time() +                diff_time = recv_time - ask_time +                timings[cur_player].append(diff_time) +                logger.info("Message received after (s): {}".format(diff_time)) +                try: +                    msg = json.loads(msg) +                except json.decoder.JSONDecodeError as err: +                    logger.debug(err) +                    continue +                if msg["type"] != "action": +                    logger.error("Unknown message: {}".format(msg)) +                    continue +                r, c = msg["location"] +                o = msg["orientation"] +                next_player = user_action(r, c, o, cur_player, +                                          cells, points, +                                          nb_rows, nb_cols) +                if points[1] + points[2] == nb_cols * nb_rows: +                    # Game over +                    winner = 1 +                    if points[2] == points[1]: +                        winner = 0 +                    if points[2] > points[1]: +                        winner = 2 +                else: +                    msg = { +                        "type": "action", +                        "game": cur_game, +                        "player": cur_player, +                        "nextplayer": next_player, +                        "score": [points[1], points[2]], +                        "location": [r, c], +                        "orientation": o +                    } +                    await websocket1.send(json.dumps(msg)) +                    await websocket2.send(json.dumps(msg)) + +                cur_player = next_player + +            # End game +            logger.info("Game ended: points1={} - points2={} - winner={}".format(points[1], points[2], winner)) +            msg = { +                "type": "end", +                "game": cur_game, +                "player": cur_player, +                "nextplayer": 0, +                "score": [points[1], points[2]], +                "location": [r, c], +                "orientation": o, +                "winner": winner +            } +            await websocket1.send(json.dumps(msg)) +            await websocket2.send(json.dumps(msg)) + +    # Timings +    for i in [1, 2]: +        logger.info("Timings: player={} - avg={} - min={} - max={}"\ +            .format(i, +                    sum(timings[i])/len(timings[i]), +                    min(timings[i]), +                    max(timings[i]))) + +    logger.info("Closed connections") + + +def user_action(r, c, o, cur_player, cells, points, nb_rows, nb_cols): +    logger.info("User action: player={} - r={} - c={} - o={}".format(cur_player, r, c, o)) +    next_player = cur_player +    won_cell = False +    cell = cells[r][c] +    if o == "h": +        if cell["h"] != 0: +            return cur_player +        cell["h"] = cur_player +        # Above +        if r > 0: +            if cells[r - 1][c]["v"] != 0 \ +                and cells[r - 1][c + 1]["v"] != 0 \ +                and cells[r - 1][c]["h"] != 0 \ +                and cells[r][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r - 1][c]["p"] = cur_player +        # Below +        if r < nb_rows: +            if cells[r][c]["v"] != 0 \ +                and cells[r][c + 1]["v"] != 0 \ +                and cells[r][c]["h"] != 0 \ +                and cells[r + 1][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c]["p"] = cur_player + +    if o == "v": +        if cell["v"] != 0: +            return cur_player +        cell["v"] = cur_player; +        # Left +        if c > 0: +            if cells[r][c - 1]["v"] != 0 \ +                and cells[r][c]["v"] != 0 \ +                and cells[r][c - 1]["h"] != 0 \ +                and cells[r + 1][c - 1]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c - 1]["p"] = cur_player +        # Right +        if c < nb_cols: +            if cells[r][c]["v"] != 0 \ +                and cells[r][c + 1]["v"] != 0 \ +                and cells[r][c]["h"] != 0 \ +                and cells[r + 1][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c]["p"] = cur_player + +    if not won_cell: +        next_player = 3 - cur_player +    else: +        next_player = cur_player +        print("Update points: player1={} - player2={}".format(points[1], points[2])) +    return next_player + + +def main(argv=None): +    parser = argparse.ArgumentParser(description='Start agent to play Dots and Boxes') +    parser.add_argument('--verbose', '-v', action='count', default=0, help='Verbose output') +    parser.add_argument('--quiet', '-q', action='count', default=0, help='Quiet output') +    parser.add_argument('--cols', '-c', type=int, default=2, help='Number of columns') +    parser.add_argument('--rows', '-r', type=int, default=2, help='Number of rows') +    parser.add_argument('--timelimit', '-t', type=float, default=0.5, help='Time limit per request in seconds') +    parser.add_argument('agents', nargs=2, metavar='AGENT', help='Websockets addresses for agents') +    args = parser.parse_args(argv) + +    logger.setLevel(max(logging.INFO - 10 * (args.verbose - args.quiet), logging.DEBUG)) +    logger.addHandler(logging.StreamHandler(sys.stdout)) + +    start_competition(args.agents[0], args.agents[1], args.rows, args.cols, args.timelimit) + + +if __name__ == "__main__": +    sys.exit(main()) + diff --git a/dotsandboxes/test/evaluate.py b/dotsandboxes/test/evaluate.py new file mode 100644 index 0000000..fb60211 --- /dev/null +++ b/dotsandboxes/test/evaluate.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python3 +# encoding: utf-8 +""" +dotsandboxescompete.py + +Template for the Machine Learning Project course at KU Leuven (2017-2018) +of Hendrik Blockeel and Wannes Meert. + +Copyright (c) 2018 KU Leuven. All rights reserved. +""" + +import sys +import argparse +import logging +import asyncio +import websockets +import json +from collections import defaultdict +import random +import uuid +import time +import csv + +logger = logging.getLogger(__name__) +OUTPUTWRITER = None + + +def start_competition(address1, address2, nb_rows, nb_cols, timelimit): +   asyncio.get_event_loop().run_until_complete(connect_agent(address1, address2, nb_rows, nb_cols, timelimit)) + + +async def connect_agent(uri1, uri2, nb_rows, nb_cols, timelimit): +    cur_game = str(uuid.uuid4()) +    winner = None +    cells = [] +    cur_player = 1 +    points = [0, 0, 0] +    timings = [None, [], []] + +    for ri in range(nb_rows + 1): +        columns = [] +        for ci in range(nb_cols + 1): +            columns.append({"v":0, "h":0, "p":0}) +        cells.append(columns) + +    logger.info("Connecting to {}".format(uri1)) +    async with websockets.connect(uri1) as websocket1: +        logger.info("Connecting to {}".format(uri2)) +        async with websockets.connect(uri2) as websocket2: +            logger.info("Connected") + +            # Start game +            msg = { +              "type": "start", +              "player": 1, +              "timelimit": timelimit, +              "game": cur_game, +              "grid": [nb_rows, nb_cols] +            } +            await websocket1.send(json.dumps(msg)) +            msg["player"] = 2 +            await websocket2.send(json.dumps(msg)) + +            # Run game +            while winner is None: +                ask_time = time.time() +                logger.info("Waiting for player {}".format(cur_player)) +                if cur_player == 1: +                    msg = await websocket1.recv() +                else: +                    msg = await websocket2.recv() +                recv_time = time.time() +                diff_time = recv_time - ask_time +                timings[cur_player].append(diff_time) +                logger.info("Message received after (s): {}".format(diff_time)) +                try: +                    msg = json.loads(msg) +                except json.decoder.JSONDecodeError as err: +                    logger.debug(err) +                    continue +                if msg["type"] != "action": +                    logger.error("Unknown message: {}".format(msg)) +                    continue +                r, c = msg["location"] +                o = msg["orientation"] +                next_player = user_action(r, c, o, cur_player, +                                          cells, points, +                                          nb_rows, nb_cols) +                if points[1] + points[2] == nb_cols * nb_rows: +                    # Game over +                    winner = 1 +                    if points[2] == points[1]: +                        winner = 0 +                    if points[2] > points[1]: +                        winner = 2 +                else: +                    msg = { +                        "type": "action", +                        "game": cur_game, +                        "player": cur_player, +                        "nextplayer": next_player, +                        "score": [points[1], points[2]], +                        "location": [r, c], +                        "orientation": o +                    } +                    await websocket1.send(json.dumps(msg)) +                    await websocket2.send(json.dumps(msg)) + +                cur_player = next_player + +            # End game +            logger.info("Game ended: points1={} - points2={} - winner={}".format(points[1], points[2], winner)) +            msg = { +                "type": "end", +                "game": cur_game, +                "player": cur_player, +                "nextplayer": 0, +                "score": [points[1], points[2]], +                "location": [r, c], +                "orientation": o, +                "winner": winner +            } +            await websocket1.send(json.dumps(msg)) +            await websocket2.send(json.dumps(msg)) + +    # Timings +    for i in [1, 2]: +        logger.info("Timings: player={} - avg={} - min={} - max={}"\ +            .format(i, +                    sum(timings[i])/len(timings[i]), +                    min(timings[i]), +                    max(timings[i]))) + +    logger.info("Closed connections") +    OUTPUTWRITER.writeln({'score1': points[1], 'score2': points[2], 'winner': winner}) + + +def user_action(r, c, o, cur_player, cells, points, nb_rows, nb_cols): +    logger.info("User action: player={} - r={} - c={} - o={}".format(cur_player, r, c, o)) +    next_player = cur_player +    won_cell = False +    cell = cells[r][c] +    if o == "h": +        if cell["h"] != 0: +            return cur_player +        cell["h"] = cur_player +        # Above +        if r > 0: +            if cells[r - 1][c]["v"] != 0 \ +                and cells[r - 1][c + 1]["v"] != 0 \ +                and cells[r - 1][c]["h"] != 0 \ +                and cells[r][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r - 1][c]["p"] = cur_player +        # Below +        if r < nb_rows: +            if cells[r][c]["v"] != 0 \ +                and cells[r][c + 1]["v"] != 0 \ +                and cells[r][c]["h"] != 0 \ +                and cells[r + 1][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c]["p"] = cur_player + +    if o == "v": +        if cell["v"] != 0: +            return cur_player +        cell["v"] = cur_player; +        # Left +        if c > 0: +            if cells[r][c - 1]["v"] != 0 \ +                and cells[r][c]["v"] != 0 \ +                and cells[r][c - 1]["h"] != 0 \ +                and cells[r + 1][c - 1]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c - 1]["p"] = cur_player +        # Right +        if c < nb_cols: +            if cells[r][c]["v"] != 0 \ +                and cells[r][c + 1]["v"] != 0 \ +                and cells[r][c]["h"] != 0 \ +                and cells[r + 1][c]["h"] != 0: +                won_cell = True +                points[cur_player] += 1 +                cells[r][c]["p"] = cur_player + +    if not won_cell: +        next_player = 3 - cur_player +    else: +        next_player = cur_player +        print("Update points: player1={} - player2={}".format(points[1], points[2])) +    return next_player + + +def main(argv=None): +    parser = argparse.ArgumentParser(description='Start agent to play Dots and Boxes') +    parser.add_argument('--verbose', '-v', action='count', default=0, help='Verbose output') +    parser.add_argument('--quiet', '-q', action='count', default=0, help='Quiet output') +    parser.add_argument('--cols', '-c', type=int, default=2, help='Number of columns') +    parser.add_argument('--rows', '-r', type=int, default=2, help='Number of rows') +    parser.add_argument('--timelimit', '-t', type=float, default=0.5, help='Time limit per request in seconds') +    parser.add_argument('--number', '-n', type=int, default=1, help='Number of games that will be played for the evaluation') +    parser.add_argument('--output', '-o', default="output.csv", help='File where game results will be written to') +    parser.add_argument('agents', nargs=2, metavar='AGENT', help='Websockets addresses for agents') +    args = parser.parse_args(argv) + +    logger.setLevel(max(logging.INFO - 10 * (args.verbose - args.quiet), logging.DEBUG)) +    logger.addHandler(logging.StreamHandler(sys.stdout)) + +    global OUTPUTWRITER +    OUTPUTWRITER = OutputWriter(args.output) + +    for i in range(args.number): +        start_competition(args.agents[0], args.agents[1], args.rows, args.cols, args.timelimit) + +    OUTPUTWRITER.close() + + +class OutputWriter: +    def __init__(self, outputfile): +        self.csvfile = open(outputfile, 'w', newline='') +        try: +            fieldnames = ['score1', 'score2', 'winner'] +            self.writer = csv.DictWriter(self.csvfile, fieldnames=fieldnames) +            self.writer.writeheader() +        except IOError: +            self.csvfile.close() + +    def writeln(self, csvdict): +        self.writer.writerow(csvdict) + +    def close(self): +        self.csvfile.close() + + +if __name__ == "__main__": +    sys.exit(main()) +  | 
