aboutsummaryrefslogtreecommitdiffstats
path: root/python/dotsandboxes
diff options
context:
space:
mode:
Diffstat (limited to 'python/dotsandboxes')
-rw-r--r--python/dotsandboxes/README.md134
-rwxr-xr-xpython/dotsandboxes/dotsandboxesagent5
-rw-r--r--python/dotsandboxes/dotsandboxesagent.py212
-rw-r--r--python/dotsandboxes/dotsandboxescompete.py212
-rw-r--r--python/dotsandboxes/dotsandboxesserver.py60
-rw-r--r--python/dotsandboxes/requirements.txt1
-rw-r--r--python/dotsandboxes/static/dotsandboxes.css10
-rw-r--r--python/dotsandboxes/static/dotsandboxes.html50
-rw-r--r--python/dotsandboxes/static/dotsandboxes.js454
9 files changed, 0 insertions, 1138 deletions
diff --git a/python/dotsandboxes/README.md b/python/dotsandboxes/README.md
deleted file mode 100644
index e3f844c..0000000
--- a/python/dotsandboxes/README.md
+++ /dev/null
@@ -1,134 +0,0 @@
-Dots and Boxes application
-==========================
-
-Live demo: https://people.cs.kuleuven.be/wannes.meert/dotsandboxes/play
-
-![Screenshot of Dots and Boxes](https://people.cs.kuleuven.be/wannes.meert/dotsandboxes/screenshot.png?v=2)
-
-This setup is part of the course "Machine Learning: Project" (KU Leuven,
-Faculty of engineering, Department of Computer Science,
-[DTAI research group](https://dtai.cs.kuleuven.be)).
-
-
-Installation
-------------
-
-The example agent is designed for Python 3.6 and requires the
-[websockets](https://websockets.readthedocs.io) package. Dependencies can be
-installed using pip:
-
- $ pip install -r requirements.txt
-
-
-Start the game GUI
-------------------
-
-This program shows a web-based GUI to play the Dots and Boxes
-game. This supports human-human, agent-human and agent-agent combinations.
-It is a simple Javascript based application that runs entirely in the browser.
-You can start it by opening the file `static/dotsandboxes.html` in a browser.
-Or alternatively, you can start the app using the included simple server:
-
- $ ./dotsandboxesserver.py 8080
-
-The game can then be played by directing your browser to http://127.0.0.1:8080.
-
-
-Start the agent client
-----------------------
-
-This is the program that runs a game-playing agent. This application listens
-to [websocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API)
-requests that communicate game information and sends back the next action it
-wants to play.
-
-Starting the agent client is done using the following command:
-
- $ ./dotsandboxesagent <port>
-
-This starts a websocket on the given port that can receveive JSON messages.
-
-The JSON messages given below should be handled by your agent.
-Take into account the maximal time allowed to reply.
-
-### Initiate the game
-
-Both players get a message that a new game has started:
-
- {
- "type": "start",
- "player": 1,
- "timelimit", 0.5,
- "grid": [5, 5],
- "game": "123456"
- }
-
-where `player` is the number assigned to this agent, `timelimit` is the
-time in seconds in which you need to send your action back to the server,
-and `grid` is the grid size in rows and columns.
-
-If you are player 1, reply with the first action you want to perform:
-
- {
- "type": "action",
- "location": [1, 1],
- "orientation": "v"
- }
-
-The field `location` is expressed as row and column (zero-based numbering) and
-`orientation` is either "v" (vertical) or "h" (horizontal).
-
-
-### Action in the game
-
-When an action is played, the message sent to both players is:
-
- {
- "type": "action",
- "game": "123456",
- "player": 1,
- "nextplayer": 2,
- "score": [0, 0],
- "location": [1, 1],
- "orientation": "v"
- }
-
-
-If it is your turn you should answer with a message that states your next
-move:
-
- {
- "type": "action",
- "location": [1, 1],
- "orientation": "v"
- }
-
-
-### Game end
-
-When the game ends after an action, the message is slightly altered:
-
- {
- "type": "end",
- "game": "123456",
- "player": 1,
- "nextplayer": 0,
- "score": [3, 1],
- "location": [1, 1],
- "orientation": "v",
- "winner": 1
- }
-
-The `type` field becomes `end` and a new field `winner` is set to the player
-that has won the game.
-
-
-Contact information
--------------------
-
-- Wannes Meert, https://people.cs.kuleuven.be/wannes.meert
-- Hendrik Blockeel, https://people.cs.kuleuven.be/hendrik.blockeel
-- Arne De Brabandere, https://people.cs.kuleuven.be/arne.debrabandere
-- Sebastijan Dumančić, https://people.cs.kuleuven.be/sebastijan.dumancic
-- Pieter Robberechts, https://people.cs.kuleuven.be/pieter.robberechts
-
diff --git a/python/dotsandboxes/dotsandboxesagent b/python/dotsandboxes/dotsandboxesagent
deleted file mode 100755
index eecf719..0000000
--- a/python/dotsandboxes/dotsandboxesagent
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-# It is not necessary to use a shell script for this. Dropping the .py
-# extension and including the correct shebang is also correct.
-python3 $(dirname "$0")/dotsandboxesagent.py $@
-
diff --git a/python/dotsandboxes/dotsandboxesagent.py b/python/dotsandboxes/dotsandboxesagent.py
deleted file mode 100644
index abf677b..0000000
--- a/python/dotsandboxes/dotsandboxesagent.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/usr/bin/env python3
-# encoding: utf-8
-"""
-dotsandboxesagent.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
-
-logger = logging.getLogger(__name__)
-games = {}
-agentclass = None
-
-
-class DotsAndBoxesAgent:
- """Example Dots and Boxes agent implementation base class.
- It returns a random next move.
-
- A DotsAndBoxesAgent object should implement the following methods:
- - __init__
- - add_player
- - register_action
- - next_action
- - end_game
-
- This class does not necessarily use the best data structures for the
- approach you want to use.
- """
- def __init__(self, player, nb_rows, nb_cols, timelimit):
- """Create Dots and Boxes agent.
-
- :param player: Player number, 1 or 2
- :param nb_rows: Rows in grid
- :param nb_cols: Columns in grid
- :param timelimit: Maximum time allowed to send a next action.
- """
- self.player = {player}
- self.timelimit = timelimit
- self.ended = False
- self.nb_rows = nb_rows
- self.nb_cols = nb_cols
- rows = []
- for ri in range(nb_rows + 1):
- columns = []
- for ci in range(nb_cols + 1):
- columns.append({"v": 0, "h": 0})
- rows.append(columns)
- self.cells = rows
-
- def add_player(self, player):
- """Use the same agent for multiple players."""
- self.player.add(player)
-
- def register_action(self, row, column, orientation, player):
- """Register action played in game.
-
- :param row:
- :param columns:
- :param orientation: "v" or "h"
- :param player: 1 or 2
- """
- self.cells[row][column][orientation] = player
-
- def next_action(self):
- """Return the next action this agent wants to perform.
-
- In this example, the function implements a random move. Replace this
- function with your own approach.
-
- :return: (row, column, orientation)
- """
- logger.info("Computing next move (grid={}x{}, player={})"\
- .format(self.nb_rows, self.nb_cols, self.player))
- # Random move
- free_lines = []
- for ri in range(len(self.cells)):
- row = self.cells[ri]
- for ci in range(len(row)):
- cell = row[ci]
- if ri < (len(self.cells) - 1) and cell["v"] == 0:
- free_lines.append((ri, ci, "v"))
- if ci < (len(row) - 1) and cell["h"] == 0:
- free_lines.append((ri, ci, "h"))
- if len(free_lines) == 0:
- # Board full
- return None
- movei = random.randint(0, len(free_lines) - 1)
- r, c, o = free_lines[movei]
- return r, c, o
-
- def end_game(self):
- self.ended = True
-
-
-## MAIN EVENT LOOP
-
-async def handler(websocket, path):
- logger.info("Start listening")
- game = None
- # msg = await websocket.recv()
- try:
- async for msg in websocket:
- logger.info("< {}".format(msg))
- try:
- msg = json.loads(msg)
- except json.decoder.JSONDecodeError as err:
- logger.error(err)
- return False
- game = msg["game"]
- answer = None
- if msg["type"] == "start":
- # Initialize game
- if msg["game"] in games:
- games[msg["game"]].add_player(msg["player"])
- else:
- nb_rows, nb_cols = msg["grid"]
- games[msg["game"]] = agentclass(msg["player"],
- nb_rows,
- nb_cols,
- msg["timelimit"])
- if msg["player"] == 1:
- # Start the game
- nm = games[game].next_action()
- print('nm = {}'.format(nm))
- if nm is None:
- # Game over
- logger.info("Game over")
- continue
- r, c, o = nm
- answer = {
- 'type': 'action',
- 'location': [r, c],
- 'orientation': o
- }
- else:
- # Wait for the opponent
- answer = None
-
- elif msg["type"] == "action":
- # An action has been played
- r, c = msg["location"]
- o = msg["orientation"]
- games[game].register_action(r, c, o, msg["player"])
- if msg["nextplayer"] in games[game].player:
- # Compute your move
- nm = games[game].next_action()
- if nm is None:
- # Game over
- logger.info("Game over")
- continue
- nr, nc, no = nm
- answer = {
- 'type': 'action',
- 'location': [nr, nc],
- 'orientation': no
- }
- else:
- answer = None
-
- elif msg["type"] == "end":
- # End the game
- games[msg["game"]].end_game()
- answer = None
- else:
- logger.error("Unknown message type:\n{}".format(msg))
-
- if answer is not None:
- print(answer)
- await websocket.send(json.dumps(answer))
- logger.info("> {}".format(answer))
- except websockets.exceptions.ConnectionClosed as err:
- logger.info("Connection closed")
- logger.info("Exit handler")
-
-
-def start_server(port):
- server = websockets.serve(handler, 'localhost', port)
- print("Running on ws://127.0.0.1:{}".format(port))
- asyncio.get_event_loop().run_until_complete(server)
- asyncio.get_event_loop().run_forever()
-
-
-## COMMAND LINE INTERFACE
-
-def main(argv=None):
- global agentclass
- 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('port', metavar='PORT', type=int, help='Port to use for server')
- args = parser.parse_args(argv)
-
- logger.setLevel(max(logging.INFO - 10 * (args.verbose - args.quiet), logging.DEBUG))
- logger.addHandler(logging.StreamHandler(sys.stdout))
-
- agentclass = DotsAndBoxesAgent
- start_server(args.port)
-
-
-if __name__ == "__main__":
- sys.exit(main())
-
diff --git a/python/dotsandboxes/dotsandboxescompete.py b/python/dotsandboxes/dotsandboxescompete.py
deleted file mode 100644
index ee2aee8..0000000
--- a/python/dotsandboxes/dotsandboxescompete.py
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/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/python/dotsandboxes/dotsandboxesserver.py b/python/dotsandboxes/dotsandboxesserver.py
deleted file mode 100644
index 1b66372..0000000
--- a/python/dotsandboxes/dotsandboxesserver.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/usr/bin/env python3
-# encoding: utf-8
-"""
-dotsandboxesserver.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 http.server
-import socketserver
-import json
-
-logger = logging.getLogger(__name__)
-
-
-class RequestHandler(http.server.SimpleHTTPRequestHandler):
- def do_GET(self):
- if self.path == "/":
- self.send_response(302)
- self.send_header("Location", "static/dotsandboxes.html")
- self.end_headers()
- return super().do_GET()
-
- def do_PUT(self):
- response = {
- 'result': 'ok'
- }
- self.send_response(200)
- self.send_header('Content-type', 'application/json')
- self.end_headers()
- self.wfile.write(json.dumps(response).encode())
-
-
-def start_server(port):
- with socketserver.TCPServer(("", port), RequestHandler) as httpd:
- print("Running on http://127.0.0.1:{}".format(port))
- httpd.serve_forever()
-
-
-def main(argv=None):
- parser = argparse.ArgumentParser(description='Start server 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('port', metavar='PORT', type=int, help='Port to use for server')
- args = parser.parse_args(argv)
-
- logger.setLevel(max(logging.INFO - 10 * (args.verbose - args.quiet), logging.DEBUG))
- logger.addHandler(logging.StreamHandler(sys.stdout))
-
- start_server(args.port)
-
-
-if __name__ == "__main__":
- sys.exit(main())
diff --git a/python/dotsandboxes/requirements.txt b/python/dotsandboxes/requirements.txt
deleted file mode 100644
index 14774b4..0000000
--- a/python/dotsandboxes/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-websockets
diff --git a/python/dotsandboxes/static/dotsandboxes.css b/python/dotsandboxes/static/dotsandboxes.css
deleted file mode 100644
index 71b1d3b..0000000
--- a/python/dotsandboxes/static/dotsandboxes.css
+++ /dev/null
@@ -1,10 +0,0 @@
-
-.footer {
- color: #B3B3B3;
- margin-bottom: 1ex;
-}
-
-.footer a {
- color: #87A0B3;
-}
-
diff --git a/python/dotsandboxes/static/dotsandboxes.html b/python/dotsandboxes/static/dotsandboxes.html
deleted file mode 100644
index 4e97508..0000000
--- a/python/dotsandboxes/static/dotsandboxes.html
+++ /dev/null
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<html>
-<html lang="en">
-<meta charset="utf-8">
-<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-<title>Dots and Boxes</title>
-<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
-<link rel="stylesheet" href="dotsandboxes.css">
-</head>
-<body>
- <div class="container">
- <h1>Dots and Boxes</h1>
- <div class="row">
- <div class="col-md">
- <div id="playing-area"></div>
- </div>
- <div class="col-md">
- <div class="form-group">
- <p>Size of game:</p>
- <div class="input-group">
- <div class="input-group-prepend">
- <span class="input-group-text">Rows and Columns</span>
- </div>
- <input type="number" class="form-control" id="nb-rows" value=6>
- <input type="number" class="form-control" id="nb-cols" value=6>
- </div>
- </div>
- <div class="form-group">
- <p>Players:</p>
- <div class="input-group mb-3">
- <div class="input-group-prepend"><span class="input-group-text" id="basic-addon3">Agent 1</span></div>
- <input type="text" class="form-control" id="agent1" aria-describedby="basic-addon3">
- </div>
- <div class="input-group mb-3">
- <div class="input-group-prepend"><span class="input-group-text" id="basic-addon3">Agent 2</span></div>
- <input type="text" class="form-control" id="agent2" aria-describedby="basic-addon3">
- </div>
- <p>Fill in the address where an agent can be reached using WebSockets (e.g. ws://127.0.0.1:8089).
- If a field is empty a human player is assumed.
- </p>
- <button type="button" class="btn btn-secondary" id="restart-btn">Restart game</button>
- </div>
- </div>
- </div>
- </div>
- <script src="https://d3js.org/d3.v4.min.js"></script>
- <script src="dotsandboxes.js"></script>
-</body>
-</html>
-
diff --git a/python/dotsandboxes/static/dotsandboxes.js b/python/dotsandboxes/static/dotsandboxes.js
deleted file mode 100644
index 11e9447..0000000
--- a/python/dotsandboxes/static/dotsandboxes.js
+++ /dev/null
@@ -1,454 +0,0 @@
-/**
- * dotsandboxes.js
- *
- * 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.
- **/
-
-function generateGuid() {
- var result, i, j;
- result = '';
- for(j=0; j<32; j++) {
- if( j == 8 || j == 12|| j == 16|| j == 20)
- result = result + '-';
- i = Math.floor(Math.random()*16).toString(16).toUpperCase();
- result = result + i;
- }
- return result;
-}
-
-// GAME LOGIC
-
-var cur_game = generateGuid();
-var cur_player = 1;
-var cur_ended = false;
-var points = [0, 0, 0];
-var timelimit = 0.5;
-var nb_cols = 6;
-var nb_rows = 6;
-var data = new Array(0);
-
-function restart_game() {
- //console.log("Restarting game");
- cur_game = generateGuid();
- nb_cols = parseInt(document.getElementById('nb-cols').value);
- if (nb_cols == "" || isNaN(nb_cols)) {
- nb_cols = 6;
- }
- nb_rows = parseInt(document.getElementById('nb-rows').value);
- if (nb_rows == "" || isNaN(nb_rows)) {
- nb_rows = 6;
- }
- cur_ended = false;
- console.log("Starting game", cur_game);
- points = [0, 0, 0];
- cur_player = 1;
- var old_length = 0;
- for (var ri=0; ri<nb_rows + 1; ri++) {
- if (ri >= data.length) {
- data.push(new Array(0));
- }
- var row = data[ri];
- for (var ci=0; ci<nb_cols + 1; ci++) {
- if (ci >= row.length) {
- row.push({l:0, t:0, p:0, r:0, c:0});
- }
- var l = 0;
- var t = 0;
- var p = 0;
- if (ri == nb_rows) {
- l = undefined;
- p = undefined;
- }
- if (ci == nb_cols) {
- t = undefined;
- p = undefined
- }
- var cell = row[ci];
- cell.l = l;
- cell.t = t;
- cell.p = p;
- cell.r = ri;
- cell.c = ci;
- }
- old_length = row.length;
- for (var ci=nb_cols + 1; ci<old_length; ci++) {
- row.pop();
- }
- }
- old_length = data.length;
- for (var ri=nb_rows + 1; ri<old_length; ri++) {
- data.pop();
- }
-}
-
-function user_click(cell, o) {
- if (cur_ended) {
- //console.log('Game ended, ignoring click');
- return;
- }
- console.log('User click', cell, o);
- var won_cell = false;
- var c = cell.c;
- var r = cell.r;
- var msg = {
- type: "action",
- game: cur_game,
- player: cur_player,
- nextplayer: cur_player,
- score: [points[1], points[2]],
- location: [r, c],
- orientation: o
- };
- if (o == "h") {
- if (cell.t != 0) {
- return;
- }
- cell.t = cur_player;
- // Above
- if (r > 0) {
- if (data[r - 1][c].l != 0
- && data[r - 1][c + 1].l != 0
- && data[r - 1][c].t != 0
- && data[r][c].t != 0) {
- won_cell = true;
- points[cur_player] += 1;
- data[r - 1][c].p = cur_player;
- }
- }
- // Below
- if (r < nb_rows) {
- if (data[r][c].l != 0
- && data[r][c + 1].l != 0
- && data[r][c].t != 0
- && data[r + 1][c].t != 0) {
- won_cell = true;
- points[cur_player] += 1;
- data[r][c].p = cur_player;
- }
- }
- }
-
- if (o == "v") {
- if (cell.l != 0) {
- return;
- }
- cell.l = cur_player;
- // Left
- if (c > 0) {
- if (data[r][c - 1].l != 0
- && data[r][c].l != 0
- && data[r][c - 1].t != 0
- && data[r + 1][c - 1].t != 0) {
- won_cell = true;
- points[cur_player] += 1;
- data[r][c - 1].p = cur_player;
- }
- }
- // Right
- if (c < nb_cols) {
- if (data[r][c].l != 0
- && data[r][c + 1].l != 0
- && data[r][c].t != 0
- && data[r + 1][c].t != 0) {
- won_cell = true;
- points[cur_player] += 1;
- data[r][c].p = cur_player;
- }
- }
- }
-
- msg["score"] = [points[1], points[2]];
-
- if (!won_cell) {
- cur_player = 3 - cur_player;
- msg.nextplayer = cur_player;
- }
- update_board();
- if (points[1] + points[2] == nb_cols * nb_rows) {
- // Game over
- var winner = 1
- if (points[2] == points[1]) {
- winner = 0;
- }
- if (points[2] > points[1]) {
- winner = 2;
- }
- cur_ended = true;
- msg.type = "end";
- msg.nextplayer = 0;
- msg.winner = winner;
- }
- send_to_agents(msg);
-}
-
-var field_margin = 10;
-var cell_width = 40;
-var cell_margin = 4;
-var player_height = 40;
-var width = 400;
-var height = 600;
-var line_width = 5;
-
-var player_color = [
- "#E6E6E6",
- "#FC6666",
- "#0F80FF"
-];
-
-var svg = d3.select("#playing-area").append("svg")
- .attr("width", width)
- .attr("height", height)
- .append("g")
- .attr("transform", "translate("+field_margin+","+field_margin+")");
-
-var player = svg.append("g")
- .attr("class", "player")
- .attr("transform", "translate(0,10)");
-
-var field = svg.append("g")
- .attr("class", "field")
- .attr("transform", "translate(0,"+player_height+")");
-
-
-function update_board() {
- // PLAYERS - enter & update
- var player_text = player.selectAll("text")
- .data([cur_player, cur_player]);
-
- player_text = player_text.enter().append("text")
- .attr("x", function(c, i) { return i * 100;})
- .merge(player_text)
- .text(function(c, i) {return "Player " + (i + 1) + ": "+points[i + 1];})
- .attr("fill", function(c, i) {
- if (c == i + 1) {
- return player_color[c];
- } else {
- return player_color[0];
- }
- });
-
- // ROWS - enter & update
- var rows = field.selectAll(".row")
- .data(data)
- .attr("fill", function() {return null;});
-
- rows.exit().remove();
-
- rows = rows.enter().append("g")
- .attr("class", "row")
- .attr("transform", function(row, i) {return "translate(0," + cell_width * i + ")";})
- .merge(rows);
-
- // COLS - enter & update
- var cols = rows.selectAll(".col")
- .data(function(col) {return col;});
-
- cols.exit().remove();
-
- var cols_enter = cols.enter().append("g")
- .attr("class", "col")
- .attr("transform", function(col, ri) {return "translate("+cell_width * ri+",0)";});
-
- // CELL - enter
- cols_enter.append("rect")
- .attr("class", "cell")
- .attr("rx", cell_margin)
- .attr("ry", cell_margin)
- .attr("opacity", 0.25)
- .attr("x", cell_margin)
- .attr("y", cell_margin)
- .attr("width", cell_width - 2*cell_margin)
- .attr("height", cell_width - 2*cell_margin);
-
- // HLINE - enter
- cols_enter.append("line")
- .attr("class", "hline")
- .attr("x1", function(cell, ci) {return cell_margin;})
- .attr("x2", function(cell, ci) {return cell_width - cell_margin;})
- .attr("y1", 0)
- .attr("y2", 0)
- .attr("stroke-linecap", "round")
- .attr("stroke", function(cell) {return player_color[cell.t];});
-
- cols_enter.append("path")
- .attr("d", "M"+cell_margin+",0"+
- "L"+(cell_width/2)+",-"+(cell_width/3)+
- "L"+(cell_width-cell_margin)+",0"+
- "L"+(cell_width/2)+","+(cell_width/3)+"Z")
- .attr("stroke", "black")
- .attr("stroke-width", 2)
- .attr("opacity", "0")
- .on("click", function(cell) {
- if (agents[cur_player].active == true) {
- console.log("Ignoring click, automated agent")
- } else {
- user_click(cell, "h");
- }
- });
-
- // VLINE - enter
- cols_enter.append("line")
- .attr("class", "vline")
- .attr("y1", function(cell, ci) {return cell_margin;})
- .attr("y2", function(cell, ci) {return cell_width - cell_margin;})
- .attr("x1", 0)
- .attr("x2", 0)
- .attr("stroke-linecap", "round")
- .attr("stroke", function(cell) {return player_color[cell.l];});
-
- cols_enter.append("path")
- .attr("d", "M0,"+cell_margin+
- "L-"+(cell_width/3)+","+(cell_width/2)+
- "L0,"+(cell_width-cell_margin)+
- "L"+(cell_width/3)+","+(cell_width/2)+"Z")
- .attr("stroke", "black")
- .attr("stroke-width", 2)
- .attr("opacity", "0")
- .on("click", function(cell) {
- if (agents[cur_player].active == true) {
- console.log("Ignoring click, automated agent");
- } else {
- user_click(cell, "v");
- }
- });
-
- cols = cols_enter
- .merge(cols);
-
- // HLINE - update
- cols.selectAll(".hline")
- .attr("stroke-width", function(cell) {
- if (typeof(cell.t) == "undefined") {
- return 0;
- }
- return line_width;
- })
- .attr("stroke", function(cell) {return player_color[cell.t];});
-
- // VLINE - update
- cols.selectAll(".vline")
- .attr("stroke-width", function(cell, ci) {
- if (typeof(cell.l) == "undefined") {
- return 0;
- }
- return line_width;
- })
- .attr("stroke", function(cell) {return player_color[cell.l];});
-
- // CELL - update
- cols.selectAll(".cell")
- .attr("fill", function(cell) {
- if (cell.p == undefined) {
- return "white";
- }
- return player_color[cell.p];
- });
-}
-
-
-// AGENT CONNECTIONS
-
-var agents = [
- {},
- {address: undefined, active: false, socket: undefined},
- {address: undefined, active: false, socket: undefined}
-];
-
-var msg_queue = [];
-
-
-function start_connections() {
- for (var i=1; i<3; i++) {
- agents[i] = {address:undefined, active: false, socket: undefined};
- var address = document.getElementById('agent'+i).value;
- if (address != "") {
- //console.log("Starting websocket for agent "+i+" on address "+address);
- var agent = agents[i];
- agent.address = address;
- agent.socket = new WebSocket(address);
- agent.socket.onopen = (function (ii, iagent) { return function(event) {
- console.log("Agent "+ii+" connected")
- iagent.active = true;
- iagent.socket.onmessage = function(event) {
- var msg = JSON.parse(event.data);
- //console.log("Get msg from agent "+ii, msg);
- if (msg.type == "action") {
- if (cur_player == ii) {
- console.log("Received action from ACTIVE player "+ii, msg);
- user_click(data[msg.location[0]][msg.location[1]], msg.orientation);
- } else {
- console.log("Received action from NON-ACTIVE player "+ii, msg);
- }
- }
- return false;
- };
- iagent.socket.onclose = function(event) {
- console.log("Closing connection to agent "+ii);
- };
- iagent.socket.onerror = function(event) {
- console.log("Error on connection to agent "+ii, event);
- };
- msg = {
- "type": "start",
- "player": ii,
- "timelimit": timelimit,
- "game": cur_game,
- "grid": [nb_rows, nb_cols]
- };
- iagent.socket.send(JSON.stringify(msg));
- };}(i, agent));
- }
- }
-}
-
-
-function send_to_agents(msg) {
- msg_queue.push(JSON.stringify(msg));
- try_sending_to_agents();
-}
-
-
-function try_sending_to_agents() {
- var all_connected = true;
- for (var i=1; i<3; i++) {
- if (agents[i].address !== undefined && agents[i].active == false) {
- all_connected = false;
- break;
- }
- }
- if (!all_connected) {
- // Wait until all are connected
- setTimeout(try_sending_to_agents, 100);
- } else {
- if (msg_queue.length == 0 ) {
- return;
- }
- var msg = msg_queue.shift();
- console.log("Send msg to agents", msg);
- for (var i=1; i<3; i++) {
- if (agents[i].active == true) {
- agents[i].socket.send(msg);
- }
- }
- }
-}
-
-
-// STARTUP
-
-function restart() {
- restart_game();
- update_board();
- start_connections();
-}
-
-var restartbtn = document.getElementById("restart-btn");
-restartbtn.onclick = function() {
- console.log("Restart game");
- restart();
-};
-
-restart();