我目前正在尝试设计一个简单的Swiss Style Tournament生成器,作为一种玩jquery并满足我自己的好奇心的方法。但是我遇到了一些障碍。
我已经建立了一个算法,根据胜利,平局和失败的数量对球员进行排序,但是我需要一些方法来阻止球员与同一个对手进行两次匹配。
目前,玩家存储在阵列中,偶数玩家与奇数玩家匹配(例如,playersarray[0]
vs playersarray[1]
,playersarray[2]
vs playersarray[3]
等等。数组中的每个玩家都是对象,其属性为name
,win
,draw
,loss
,lostGame
(用于确定丢失的人,两者都丢失了draw)和hasPlayed
(存储他们已经按姓名匹配的人)。
这是我现在的排序algorthim:
//---SORTING ARRAYS----
//Danish Style (Players can play the same Player twice)
//Sort loss to bottom
playersArray.sort(function(a,b){
return parseFloat(a.loss) - parseFloat(b.loss);
});
//Sort Draw to top
playersArray.sort(function(a,b){
return parseFloat(b.draw) - parseFloat(a.draw);
});
//Sort with wins on top
playersArray.sort(function(a,b){
return parseFloat(b.win) - parseFloat(a.win);
});
//Swiss Style (make it so Players can't play the same opponet twice)
for(var j = 0; j < playersArray.length; j +=2){
if($.inArray(playersArray[j].name, playersArray[j+1].haveplayed) != -1){
var tempI = playersArray[j];
playersArray[j] = playersArray[j-1];
playersArray[j-1] = tempI;
console.log("Matched!");
}
}
我现在的解决方案非常糟糕,因为它只是将玩家换成一个,如果0和1位玩家匹配,则不会起作用。
任何有关我如何能够解决这个问题的见解都会受到赞赏,因为它对我来说似乎是一个很大的麻烦。
答案 0 :(得分:2)
首先,我不一定要比较获胜/平局/亏损计数,而是一般得分:
赢得+1,抽取+0,失去-1
总分也有一个好处,可以让您在检查“近似分数”规则时更轻松:
在随后的回合中,每个参赛者都会面对具有相同或几乎相同累积分数的对手。
接下来,我会稍微破解代码 - 但这是个人偏好。如果你有一个快速的小algorythm总是很好,但从长远来看,它可能会损害可读性并使事情变得复杂。你为什么不在Player实例中存储过去的比赛,并检查两名球员是否已经有比赛(甚至存储结果)?
我写了一个小例子,将其分解为更清晰的组件(匹配和播放器)。当然,这可以进一步优化和缩短,但它主要以这种方式写出,以便您清楚地了解每一步。它基于提供的维基页面上的规则。
https://jsfiddle.net/ovkktbg6/5/
// settings
var MAX_SCORE_DIFFERENCE = 2;
// match
function Match(player1, player2, result) {
this.player1 = player1
this.player2 = player2
// player1 [won/draw/lost] against player2
this.result = result
// give each player the match results
this.player1.addMatch(this)
this.player2.addMatch(this)
console.log(player1.name, result == Match.RESULT_WIN ? 'won' : ( result == Match.RESULT_LOSS ? 'lost' : 'draw' ), 'against', player2.name, '-', player1.score, ':', player2.score)
}
// possible results
Match.RESULT_WIN = 1
Match.RESULT_DRAW = 0
Match.RESULT_LOSS = -1
Match.randomResult = function() {
return Math.floor( Math.random() * (Match.RESULT_WIN - Match.RESULT_LOSS + 1) ) + Match.RESULT_LOSS;
}
// player
function Player(name) {
this.name = name // just to have any identification
this.score = 0
this.wins = 0
this.losses = 0
this.draws = 0
this.matches = []
}
Player.prototype.addMatch = function( match ) {
this.matches.push(match)
if( match.result == Match.RESULT_DRAW ) {
this.draws++;
} else {
// check if the first player is this one
if( match.player1 == this ) {
// this player1 has WON against player2
if(match.result == Match.RESULT_WIN) {
this.wins++;
this.score++;
} else {
this.wins--;
this.score--;
}
// this player is player2
} else {
// player1 has LOST against this player2
if(match.result == Match.RESULT_LOSS) {
this.wins++;
this.score++;
} else {
this.wins--;
this.score--;
}
}
}
}
Player.prototype.hasPlayedAgainst = function( player ) {
// a player canot play against him/herself
if( this == player ) return false;
// check the other matches
for( var i = 0; i < this.matches.length; i++ ) {
var match = this.matches[i]
if( match.player1 == player || match.player2 == player) return true;
}
return false;
}
// example
var playerList = []
playerList.push( new Player('Alex') )
playerList.push( new Player('Bob') )
playerList.push( new Player('Charles') )
playerList.push( new Player('David') )
playerList.push( new Player('Erik') )
playerList.push( new Player('Franz') )
playerList.push( new Player('Georg') )
playerList.push( new Player('Hans') )
playerList.push( new Player('Ian') )
playerList.push( new Player('Jacob') )
playerList.push( new Player('Karl') )
playerList.push( new Player('Lars') )
playerList.push( new Player('Marco') )
// if the matchups should be random each time, the playerList can be:
// randomly ordered once, here - the tournament will have random matchups
// or randomly ordered inside the while() loop. Every tournament round will have random matchups then
// play through the tournament
// pick every player in the playerList and match them against every other player
var round = 0
var matchPossible = true
while( matchPossible ) {
// this flag is set to true if there was a match
// if no match was played, that means that the tournament is over, since every player already competed against each other or there are no similar enough scores to play
matchPossible = false;
// this loop goes through the whole player list the first time, picking player1
for( var i = 0; i < playerList.length; i++ ) {
var player1 = playerList[i];
// exclude players who already played this round
if( player1.matches.length > round ) {
continue;
}
// this loop goes through the whole player list once more, picking player2
// the two loops match every player against every player, skipping their match with the conditions below
for( var ii = 0; ii < playerList.length; ii++ ) {
var player2 = playerList[ii];
// do not play against him/herself
if( player1 == player2 ) {
continue;
}
// exclude players who already played this round
if( player2.matches.length > round ) {
continue;
}
// the two already faced each other
if( player1.hasPlayedAgainst( player2 ) ) {
continue;
}
// their scores are too far apart
if( Math.abs( player1.score - player2.score ) > MAX_SCORE_DIFFERENCE ) {
continue;
}
// there was a match!
matchPossible = true
// if we get here, the players should face each other
new Match( player1, player2, Match.randomResult() );
// break out, let the next players have a match
break;
}
}
// start the next round
round++;
}
// the tournament ended - let's rank them.
playerList.sort( function( player1, player2 ) {
return player2.score - player1.score;
} )
// print the results
console.log('-----')
for( var i = 0; i < playerList.length; i++ ) {
var player = playerList[i];
console.log('Rank', i + 1, ':', player.name, 'with', player.score, 'points');
}
编辑这样分解它的另一个好处就是你不必太担心聪明的排序算法和类似的东西 - 每个组件都按照给定的方式完成它的工作规则,最后你评估结果。
它没有经过深入测试,所以很可能它仍然有一些隐藏的错误,但通常它应该可以正常工作,根据我做的几个测试。
编辑:根据要求,我会尝试进一步解释循环。两个for()
循环基本上只做第一次选择player1,然后将player1与playerList中的每个玩家匹配(每个玩家都称为player2)。当当前的player1与每个player2匹配时,选择下一个player1。
假设我们有Alex作为player1:
Alex (player1) is matched against Alex (player2) [no match - same player]
Alex (player1) is matched against Bob (player2) [no match - already had a match]
Alex (player1) is matched against Charles (player2) - they have a match!
Now the nested `for( var ii )` loop is broken with `break;`, and Alex (player1) is done for the moment. We pick the next player in the playerList:
Bob (player1) is matched against Alex (player2) [no match - already had a match]
Bob (player1) is matched against Bob (player2) [no match - same player]
Bob (player1) is matched against Charles (player2) [no match - charles already had a match this round]
Bob (player1) is matched against David (player2) - they have a match!
Now the nested `for( var ii)` loop breaks a second time, and we continue with Charles
Charles (player1) is matched against Alex (player2) [no match - already had a match]
etc...
同时,while( ... )
循环经历了每一轮比赛:每一轮,所有玩家都互相核对,直到我们达到一个不允许玩家与任何其他玩家对战的点。锦标赛结束了,我们可以根据分数对playerList进行排序。
修改(2):只是为了更好地使用continue
和break
关键字来形象化我的解释,因为评论很难做到:(请记住,这只是用于演示目的的伪代码。)
继续强>
for( loop A ) {
for( loop B ) {
continue; // just skip to the end of loop B, and go through loop B again!
... this stuff is skipped by continue ...
END OF LOOP <- continue gets us here! loop A is unaffected
}
}
<强>额强>
for( loop A ) {
for( loop B ) {
break; // completly jump out of loop B!
... this stuff is skipped by break ...
}
<- break gets us here! loop B is broken out of, we just continue with the code in loop A
... stuff that is NOT skipped by break! ...
}