我目前正在尝试设计一个简单的Swiss Style Tournament生成器,作为一种玩转jquery并满足自己好奇心的方法但是我遇到了一些障碍。
我已经建立了一个算法,可以根据赢、平和输的次数在回合结束时对玩家进行排序,但是我需要一些方法来防止玩家与同一个对手匹配两次。
目前,这些播放器存储在一个数组中,偶数播放器与奇数播放器匹配(例如,playersarray[0]
vsplayersarray[1]
,playersarray[2]
vsplayersarray[3]
等等)。数组中的每个玩家都具有属性name
、win
、draw
、loss
、lostGame
、hasPlayed
(用于确定谁输了,两个都输了是抽签)和(存储已按名称匹配的人)的属性和对象。
这是我现在的排序算法:
//---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号位置的球员匹配,就不起作用。
任何关于我如何解决这个问题的见解都是值得赞赏的,因为这对我来说似乎是一个很大的麻烦。
最佳答案
首先,我不一定要比较赢/平/输计数,而是比较一个总得分:
赢+1,平+0,输-1
一般分数也有助于您在检查“接近分数”规则时获得更轻松的时间:
在随后的回合中,每个参赛者面对一个累积得分相同或几乎相同的对手。
接下来,我会把代码分解一点——但这是个人的偏好。如果你有一个快速的小算法总是很好的,但是从长远来看它会损害可读性并使事情复杂化为什么不把过去的对决存储在Player实例中,并检查两个玩家是否已经进行了对决(甚至存储结果)?
我写了一个小例子,把它分解成一些更清晰的组件(比赛和球员)。当然,这可以进一步优化和缩短,但它主要是这样写出来的,给你一个清晰的视野,每一步。它基于wiki页面上提供的规则。
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。假设我们有亚历克斯作为玩家1:
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( ... )
循环贯穿于比赛的每一轮:每一轮,所有的球员都要互相检查,直到我们达到一个点,即任何球员都不允许与任何其他球员比赛比赛到此结束,我们可以根据比分对选手进行排序。编辑(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! ...
}