diff options
Diffstat (limited to 'dotsandboxes/web')
| -rw-r--r-- | dotsandboxes/web/dotsandboxes.css | 10 | ||||
| -rw-r--r-- | dotsandboxes/web/dotsandboxes.html | 50 | ||||
| -rw-r--r-- | dotsandboxes/web/dotsandboxes.js | 454 | 
3 files changed, 514 insertions, 0 deletions
diff --git a/dotsandboxes/web/dotsandboxes.css b/dotsandboxes/web/dotsandboxes.css new file mode 100644 index 0000000..71b1d3b --- /dev/null +++ b/dotsandboxes/web/dotsandboxes.css @@ -0,0 +1,10 @@ + +.footer { +  color: #B3B3B3; +  margin-bottom: 1ex; +} + +.footer a { +  color: #87A0B3; +} + diff --git a/dotsandboxes/web/dotsandboxes.html b/dotsandboxes/web/dotsandboxes.html new file mode 100644 index 0000000..4e97508 --- /dev/null +++ b/dotsandboxes/web/dotsandboxes.html @@ -0,0 +1,50 @@ +<!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/dotsandboxes/web/dotsandboxes.js b/dotsandboxes/web/dotsandboxes.js new file mode 100644 index 0000000..11e9447 --- /dev/null +++ b/dotsandboxes/web/dotsandboxes.js @@ -0,0 +1,454 @@ +/** + * 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();  | 
