Skip to content

Instantly share code, notes, and snippets.

@ninewise
Created November 7, 2014 16:42
Show Gist options
  • Save ninewise/2d8d7a09d02d5044c3d4 to your computer and use it in GitHub Desktop.
Save ninewise/2d8d7a09d02d5044c3d4 to your computer and use it in GitHub Desktop.
Easy tournament bracket, double elimination
function Node() {
this.left = undefined;
this.right = undefined;
this.person = undefined;
};
function makeWinners(people, size) {
var root = new Node();
if(people.length === 1 && (size === 1 || size === 2)) {
root.person = people[0];
} else {
root.left = makeWinners(people.slice(0, Math.round(people.length / 2)), size / 2);
root.right = makeWinners(people.slice(Math.round(people.length / 2), people.length), size / 2);
}
return root;
};
function mergeLosers(left, right) {
var i;
var losers = [];
losers.push(left[0].concat(right[0]));
for(i = 1; i < left.length; i++) {
losers.push((right[i] || []).concat((left[i]) || []));
}
return losers;
};
function _makeLosers(size, losers, depth) {
var root = new Node();
losers[depth] = losers[depth] || [];
if(size === 1) {
losers[depth] = [root].concat(losers[depth]);
} else if(size === 2) {
root.left = new Node();
root.right = new Node();
losers[depth] = [root.right, root.left].concat(losers[depth]);
} else {
root.left = new Node();
root.right = new Node();
root.right.left = _makeLosers(~~((size - 1) / 2), losers, depth + 1);
root.right.right = _makeLosers(Math.round((size - 1) / 2), losers, depth + 1);
losers[depth].push(root.left);
}
return root;
};
function makeLosers(size) {
var losers = [], flat = [];
var root = _makeLosers(size, losers, 0);
for(var i = 0; i < losers.length; i++) flat = flat.concat(losers[i]);
return {root: root, losers: flat};
};
function makeBracket(people) {
var root = new Node();
var size = nextPowerOfTwo(people.length);
root.left = makeWinners(people, size);
var losers = makeLosers(people.length - 1);
root.right = losers.root;
return {root: root, losers: losers.losers};
};
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/array/shuffle [v1.0]
function shuffle(o){ //v1.0
for(var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
};
function nextPowerOfTwo(n) {
var i = 1;
while(i < n) i *= 2;
return i;
};
function indexFromPath(path, size) {
var index = Math.pow(2, path.length);
var next = nextPowerOfTwo(size);
var plus = 0;
if(index === next / 2 && size <= 3 * next / 4) path = path.slice(0, path.length - 1);
for(var i = 0; i < path.length; i++) plus = 2 * plus + path[i];
return index + plus - 1;
};
function getAvailableMatches(root, path, setWinnerAndLoser) {
var matches = [];
var count = 0;
if(typeof root.left === 'undefined') return [];
if(typeof root.right === 'undefined') return [];
if(typeof root.left.person === 'undefined') {
matches = matches.concat(getAvailableMatches(root.left, path.concat([0]), setWinnerAndLoser));
count += 1;
}
if(typeof root.right.person === 'undefined') {
matches = matches.concat(getAvailableMatches(root.right, path.concat([1]), setWinnerAndLoser));
count += 1;
}
if(count === 0) {
matches.push({
left: root.left.person,
right: root.right.person,
setWinnerAndLoser: setWinnerAndLoser(root, path)
});
}
return matches;
}
function Bracket(people) {
var t = makeBracket(people);
this.size = people.length;
this.root = t.root;
this.losers = t.losers;
}
Bracket.prototype.getAvailableMatches = function() {
var self = this;
// Finale done
if(typeof this.root.person !== 'undefined') return [];
var matches = [];
if(typeof this.root.left.person === 'undefined') {
matches = matches.concat(getAvailableMatches(this.root.left, [], function(node, path) {
return function(winner, loser) {
node.person = winner;
self.losers[indexFromPath(path, self.size)].person = loser;
};
}));
}
if(typeof this.root.right.person === 'undefined') {
matches = matches.concat(getAvailableMatches(this.root.right, [], function(node, path) {
return function(winner, loser) {
node.person = winner;
};
}));
}
// FINALE
if(typeof this.root.left.person === 'undefined') return matches;
if(typeof this.root.right.person === 'undefined') return matches;
matches.push({
left: this.root.left.person,
right: this.root.right.person,
setWinnerAndLoser: function(winner, loser) {
self.root.person = winner;
}
});
return matches;
};
function drawNode(node, lines, prefix, left, winner) {
// if leaf
//var winner = typeof node.person !== 'undefined';
var dot = winner ? '▛' : '.';
var tick = winner ? '▀' : "'";
if(typeof node.left === 'undefined' && typeof node.right === 'undefined') {
var end = winner ? '▀▀▀▀▀ ' : '————o ';
lines.push(prefix + (left ? dot : tick) + end + node.person);
} else { // not leaf
var leftwinner = false, rightwinner = false;
if(typeof node.person !== 'undefined') {
leftwinner = node.person === node.left.person
rightwinner = node.person === node.right.person
} else {
leftwinner = typeof node.left.person !== 'undefined';
rightwinner = typeof node.right.person !== 'undefined';
}
var end = winner ? '▀▀▀' + (leftwinner ? '▘' : rightwinner ? '▌' : 'X') : '---x ';
var bar = winner ? '▌' : '|';
drawNode(node.left, lines, prefix + (left ? ' ' : bar) + ' ', true, leftwinner);
lines.push(prefix + (left ? dot : tick) + end);
drawNode(node.right, lines, prefix + (left ? bar : ' ') + ' ', false, rightwinner);
}
};
Bracket.prototype.draw = function(div) {
var lines = [];
drawNode(this.root, lines, "", true, typeof this.root.person !== 'undefined');
div.html("<pre>" + lines.join("\n") + "</pre>");
};
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<!--<link rel="stylesheet" type="text/css" href="style.css" />-->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="bracket.js"></script>
</head>
<body>
<div id="form">
<textarea id="people" placeholder="Participants on lines."></textarea>
<button id="startButton">Start Competition</button>
</div>
<div id="bracket">
</div>
<div id="matches">
</div>
<script type="text/javascript"><!--
var bracket;
var matches;
function drawAvailableMatches() {
var list = $("<ul></ul>");
for(var i = 0; i < matches.length; i++) {
var item = $("<li></li>");
var leftbtn = $("<button></button>");
leftbtn.html(matches[i].left);
leftbtn.addClass("left");
leftbtn.addClass("matchButton");
var rightbtn = $("<button></button>");
rightbtn.html(matches[i].right);
rightbtn.addClass("right");
rightbtn.addClass("matchButton");
var input = $('<input type="hidden" class="index" value="'+i+'"></input>');
item.append(leftbtn);
item.append(' vs. ');
item.append(rightbtn);
item.append(input);
list.append(item);
}
$("#matches").html(list);
};
$("#startButton").click(function() {
var people = $("#people").val().split("\n");
bracket = new Bracket(people);
matches = bracket.getAvailableMatches();
$("#form").remove();
bracket.draw($("#bracket"));
drawAvailableMatches();
});
$(document).on('click', '.matchButton', function() {
var winner = $(this).html();
var i = $(this).parent().find(".index").val();
if(winner === matches[i].left) {
matches[i].setWinnerAndLoser(matches[i].left, matches[i].right);
} else {
matches[i].setWinnerAndLoser(matches[i].right, matches[i].left);
}
matches = bracket.getAvailableMatches();
bracket.draw($("#bracket"));
drawAvailableMatches();
});
--></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment