javascript - 防止两名玩家在瑞士风格锦标赛中被匹配两次

标签 javascript jquery algorithm sorting

我目前正在尝试设计一个简单的 Swiss Style Tournament生成器,作为玩弄 jquery 和满足我自己好奇心的一种方式。但是我遇到了一些障碍。

我已经构建了一个算法,可以在回合结束时根据胜负数对玩家进行排序,但我需要一些方法来防止玩家与同一个对手匹配两次。

目前玩家存储在一个数组中,偶数玩家与奇数玩家匹配(例如,playersarray[0] vs playersarray[1]playersarray [2]vsplayersarray[3] 等)。数组中的每个玩家都是具有属性 namewindrawloss 的对象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

总分还有一个好处,可以让您更轻松地检查“接近分数”规则:

In subsequent rounds, each competitor faces an opponent with the same, or almost the same, cumulative score.

接下来,我将稍微分解代码 - 但这是个人喜好。如果你有一个快速的小算法总是好的,但从长远来看它会损害可读性并使事情复杂化。为什么不将过去的比赛存储在 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。

假设我们有 Alex 作为玩家 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( ... ) 循环遍历锦标赛的每一轮:每一轮,所有玩家都会相互检查,直到我们到达不允许任何玩家的点与任何其他玩家对战。届时比赛结束,我们可以根据得分对 playerList 进行排序。

编辑 (2):只是为了更好地形象化我对 continuebreak 关键字的解释,因为在评论:(请记住,这只是用于演示目的的伪代码。)

继续

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! ...
}

关于javascript - 防止两名玩家在瑞士风格锦标赛中被匹配两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31478110/

相关文章:

javascript - Ace 编辑器未在编辑器中显示 HTML 代码

javascript - 导航菜单中的重置开关

c++ - 选择高效的数据结构来寻找韵律

algorithm - 广度优先搜索 : Knight cover

javascript - 使用切换或类似功能在 Select2 中显示/隐藏选项

javascript - 无法通过无法读取 null 的属性样式

javascript - 如何安排网页中js和css文件的顺序?

javascript - Jade 模板 - 使用 URL 作为链接标签

performance - 使用 ReactJS 的优缺点

algorithm - 冒泡排序算法分析