UNPKG

comparative-judgement

Version:

Comparative Judgement Algorithms

271 lines (257 loc) 9.58 kB
/* jshint undef: false, strict: false */ // Turn off strict mode for this function so we can assign to global (function (root, factory) { /* AMD/CommonJS compatibility largely stolen from https://github.com/kriskowal/q/blob/master/q.js */ // This file will function properly as a <script> tag, or a module // using CommonJS and NodeJS or RequireJS module formats. In // Common/Node/RequireJS, the module exports the depres API and when // executed as a simple <script>, it creates a depres global instead. // CommonJS if ("object" === typeof exports) { module.exports = factory(require('underscore')); // RequireJS } else if ("function" === typeof define && define.amd) { define(["underscore"], factory); // <script> } else { root.cj = factory(root._); } })(this, function (_) { "use strict"; // Chris's selection method function selectionNonAdaptive(players) { //shuffle and sort the players to get a pseudo-random distribution players = _.sortBy(_.shuffle(players), function (player) { return player.selected; }); var player = players.shift(); var opponent; //First decision? Choose the player with the next fewest decisions if (player.selected <= 0) { opponent = players.shift(); } else { //Find a player not yet compared to opponent = _.find(players, function (candidate) { return !_.contains(candidate.opponents, player._id); }); } if (!opponent) { //All matches have been made, make a random choice opponent = players.shift(); } // Update Judges Collection with current pair information return [player, opponent]; } function selectionAdaptive(players, thr, AP, offset) { if (!offset) { offset = 0; } else { offset = (_.random(1)==1 ? offset : -offset); } var statutils = require('..').statutils; //assign true score of 0 if no true score for (var i = 0; i < players.length; i++) { if (!players[i].hasOwnProperty('trueScore')) { players[i].trueScore = 0; } } //thr = number of comparisons planned //AP = acceleration parameter thr = (thr ? thr : 20); AP = (AP ? AP : 1); //shuffle and sort the players to get a pseudo-random distribution players = _.sortBy(_.shuffle(players), function (player) { return player.selected; }); var player = players.shift(); var opponent; var opponents; //First decision? Choose the opponent with the next fewest decisions if (player.selected <= 0) { opponent = players.shift(); } else { // Calculate the weighted probabilities of selection // Items should include all the true scores of candidates not yet compared to // Find a player not yet compared to opponents = _.filter(players, function (candidate) { return !_.contains(candidate.opponents, player._id); }); if (opponents.length === 0) { // If all have been compared to, use all // console.log('all opponents used up'); opponents = players; } else { //console.log('possible opponents',opponents.length); } var items = _.pluck(opponents, 'trueScore'); //console.log('items: ',items); // Number of comparisons made by player var done = player.comparisons; // Get probabilities of selection var theta = player.trueScore + offset; //console.log('theta: ',theta); var iPr = statutils.calciPR(theta, items, done, thr, AP); //console.log('iPr: ',iPr); // Choose opponent at random using vector of probabilties var opponentIndex = statutils.sampleVector(iPr, Math.random()); opponent = opponents[opponentIndex]; } //console.log('match: ',player._id,' v ',opponent._id); return [player, opponent]; } function chained(players,mx){ var player; var opponent; var rightChoices; var leftChoices; // Left hand side // Filter out any unanchored leftChoices = _.filter(players, function(pl){return (!pl.hasOwnProperty("anchorScore") );}); var seeds = _.filter(players, function(pl){return (pl.hasOwnProperty("anchorScore") );}); // Remove any players with greater than mx selected leftChoices = _.filter(leftChoices, function(pl){return(pl.selected<mx);}); if(leftChoices.length>0){ // Shuffle and sort the players to get a pseudo-random distribution leftChoices = _.sortBy(_.shuffle(leftChoices), function (player) { return player.selected; }); player = leftChoices.shift(); if(seeds.length>0){ if(player.selected===0){ // Seed decision // Find a seed not yet compared to // Shuffle and sort the players to get a pseudo-random distribution seeds = _.sortBy(_.shuffle(seeds), function (player) { return player.selected; }); opponent = _.find(seeds, function (candidate) {return !_.contains(candidate.opponents, player._id);}); } } if(!opponent){ // No seed pair this time // Not the same player or seed rightChoices = _.filter(players, function(pl){return (pl._id!=player._id && !pl.hasOwnProperty("anchorScore"));}); // Not yet compared to rightChoices = _.shuffle(rightChoices); opponent = _.find(rightChoices, function (candidate) { return !_.contains(candidate.opponents, player._id); }); } if(!opponent){ // Try a seed // Not the same player but can be seed rightChoices = _.filter(players, function(pl){return (pl._id!=player._id);}); // Not yet compared to rightChoices = _.shuffle(rightChoices); opponent = _.find(rightChoices, function (candidate) { return !_.contains(candidate.opponents, player._id); }); } if(!opponent){ // Repeat a pair rightChoices = _.filter(players, function(pl){return (pl._id!=player._id);}); // Not yet compared to rightChoices = _.shuffle(rightChoices); opponent = rightChoices.shift(); } } return [player, opponent]; } function selectionSwiss(players) { // Takes round & players // First round – pair at random. // Shuffle and sort the players to get a pseudo-random distribution var sameRound; var opponents; players = _.sortBy(_.shuffle(players), function (player) { return player.selected; }); var player = players.shift(); var opponent; //First decision? Choose the player with the next fewest decisions if (player.selected <= 0) { opponent = players.shift(); } else { console.log('round: ', player.selected+1); // Find a player not yet compared to who is on the same round sameRound = _.filter(players, function (candidate) { return (candidate.comparisons == player.comparisons); }); if (sameRound.length === 0) { // If all have fnished round, find any sameRound = players; } console.log('same round players: ', sameRound.length); // Find a player not uet compared to // Find a player not yet compared to opponents = _.filter(sameRound, function (candidate) { return !_.contains(candidate.opponents, player._id); }); console.log('of which not yet compared: ',opponents.length); if (opponents.length === 0) { // If all have been compared to, use all console.log('all opponents used up'); opponents = players; } // Now we have opponents in the same round not yet compared to // Find one with the same or closest score // Calculate minimum absolute difference opponents = _.sortBy(opponents, function (pl) { return Math.abs(pl.observedScore - player.observedScore); }); //console.log(player, opponents); opponent = opponents.shift(); } return [player, opponent]; } function selectionByJudge(idJudge, players, decisions) { // Every judge should see each possible pair var statutils = require('..').statutils; // List of ids var pls = _.pluck(players, '_id'); // Every possible combination of ids var k_combinations = statutils.k_combinations; var combs = k_combinations(pls, 2); // Filter decisions by judge var dec = _.filter(decisions, function (decision) { return decision.judge == idJudge; }); //console.log('dec: ', dec); // Get a list of all combinations so far var pl1 = _.pluck(dec, 'chosen'); var pl2 = _.pluck(dec, 'notChosen'); //console.log('pl1: ', pl1); //console.log('pl2: ', pl2); var pairs = _.zip(pl1, pl2); //console.log('combs: ', combs); //console.log('pairs', pairs); var diff = _.filter(combs, function (obj) { return !_.findWhere(pairs, obj) & !_.findWhere(pairs, [obj[1], obj[0]]); }); //If diff, then not all pairs have been selected var pair; if (diff.length > 0) { pair = _.shuffle(diff).shift(); } else { //If all pairs chosen, choose a random pair pair = _.shuffle(combs).shift(); } var player1 = _.findWhere(players, { _id: pair[0] }); var player2 = _.findWhere(players, { _id: pair[1] }); return [player1, player2]; } return { selectionAdaptive: selectionAdaptive, selectionNonAdaptive: selectionNonAdaptive, selectionByJudge: selectionByJudge, selectionSwiss: selectionSwiss, chained: chained, }; });