Created
November 7, 2014 16:42
-
-
Save ninewise/2d8d7a09d02d5044c3d4 to your computer and use it in GitHub Desktop.
Easy tournament bracket, double elimination
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>"); | |
}; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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