var margin = { top: 50, right: 50, bottom: 50, left: 50 }, width = 1200 - margin.left - margin.right, height = 500 - margin.top - margin.bottom, nodeSize = 45; var prevTicTacToe = new ma.games.TicTacToe({ board: [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']] }); var player = prevTicTacToe.currentPlayer(); function updateUtility(node, nextNode) { if (node.game.currentPlayer() === player) { if (nextNode.utility > node.utility) { node.utility = nextNode.utility; } } else if (nextNode.utility < node.utility) { node.utility = nextNode.utility; } node.showUtility = true; } var updated = function (element, index, array) { return element.showUtility === true; } var depthFirstIteration = function(node) { if (!node) { return; } node.utility = node.game.currentPlayer() === player ? -Number.MAX_VALUE : Number.MAX_VALUE; var moves = node.game.moves(); if(!moves.length) { node.utility = ma.utilFunc(node.game, player); node.showUtility = true; } if(node.children && node.children.length === moves.length) { if (node.children.every(updated)) { for (var i = 0; i < node.children.length; i++) { updateUtility(node, node.children[i]); } } } // go to parent node if (!moves.length || (node.children && node.children.length === moves.length)) { return depthFirstIteration(node.parent); } else { if (!node.children) { node.children = []; } var child = { game: node.game.copy().move(node.children.length), parent: node, id: nodes.length, showUtility: false }; node.children.push(child); return child; } }; var ticTacToe = new ma.games.TicTacToe({ board: [['O', 'X', ' '], [' ', 'X', 'O'], [' ', ' ', 'X']] }); var svgView = new ma.views.TicTacToeSVG({ model: ticTacToe, sideLength: nodeSize }); var diagonal = d3.svg.diagonal() .projection(function(d) { return [d.x, d.y]; }); var svg = d3.select("body") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .attr("style", "background-color: #eeeeee") .append("g") .attr("transform", "translate(" + margin.left + ", " + margin.top + ")"); var tree = d3.layout.tree().size([width, height]); tree.separation(function(a, b) { return 2; }); var root = { game: ticTacToe, id: 0, utility: 0, showUtility: false }; var nodes = tree(root); root.px = root.x; root.py = root.y; var curNode = root; var update = function() { if (!curNode) { clearInterval(timer); } nodes.push(curNode); console.log(curNode); // Enter nodes var displayNodes = svg.selectAll(".node-group") .data(tree.nodes(root), function (d) { return d.id; }); displayNodes.enter() .append("g") .attr("class", "node-group") .attr("transform", function (d) { svgView.model = d.game; svgView.svg = d3.select(this); svgView.render(); if (!d.parent) { return "translate(" + ((width / 2) - (nodeSize / 2)) + ", " + (0 - (nodeSize / 2)) + ")"; } return "translate(" + (d.parent.px - (nodeSize / 2)) + ", " + (d.parent.py - (nodeSize / 2)) + ")"; }); displayNodes.append("text") .attr("x", "-10") .attr("y", "-10") .attr("dy", ".15em") .text(function (d) { return d.showUtility ? d.utility : ""; }); // Enter links if(curNode) { svg.selectAll(".link") .data(tree.links(nodes), function (d) { return d.source.id + "-" + d.target.id; }) .enter() .insert("path", ".node-group") .attr("class", "link") .attr("d", function (d) { var o = { x: d.source.px, y: d.source.py }; return diagonal({ source: o, target: o }); }) .attr("fill", "none") .attr("stroke", "#666666") .attr("stroke-width", 2); } var t = svg.transition().duration(250); // Update links t.selectAll(".link") .attr("d", diagonal); // Update nodes t.selectAll(".node-group") .attr("transform", function (d) { d.px = d.x; d.py = d.y; return "translate(" + (d.x - (nodeSize / 2)) + ", " + (d.y - (nodeSize / 2)) + ")" }); if (curNode) { curNode = depthFirstIteration(curNode); } }; var duration = 250, timer = setInterval(update, duration);