var INSTRUCTION_ROUND = 10; var FIRST_ACTUAL_ROUND = 101; var max_score = 1000; var scale = 900; var scaleFactor = 1.0; // divide the scale by this var num_rounds = 15; var total_players = 16; // TODO: get from variable var network = []; // we don't want player 1 to always be network[1] because when there are mostly bots, the player will always be in the same spot. So, we use a radix to shift the location var playerRadix = 0; // converts between playerId and networkId. var myNetworkId = 1; var myNetwork = []; // in playerId, not networkId var network_types = ["a","b","c","d","e","f","g","h"]; // load from variable var network_type = "a"; // load from above var min_distance2 = 0; //25; // square of: can't put in a new well closer than this var showTeamModulo = 1; var map_num = 1; var num_maps = 50; var showScoreWhenNotMap = false; var drilling_history = 1; var click_color = "#000000"; var instruction_timeout = 45; var round_seconds = 30; var total_score = 0; var num_channels=0; //total number of channels available var num_channels_select=1; //how many channels to turn on each round var neighborPoints = []; var yourPoints = []; var botBehavior = {}; // playerId => roundNum => function var showScore = true; // changed to true/false var showMap = true; // changed to true/false function initializeGame() { try { instruction_timeout = parseInt(variables['num_channels_select']); } catch (err) { } try { instruction_timeout = parseInt(variables['instruction_timeout']); } catch (err) { } try { total_players = parseInt(variables['total_players']); } catch (err) { alert(err); } try { round_seconds = parseInt(variables['round_seconds']); if (isNaN(round_seconds)) { round_seconds = 30; } } catch (err) { alert(err); } $("#i_seconds").html(round_seconds); // update the in-game clock too var seconds = round_seconds; var mins = Math.floor(seconds/60); var secondRemainder = seconds%60; var secondsStr = ('0'+secondRemainder).slice(-2); // add leading zero, then use last 2 digits $("#timer .countdown-clock").html(mins+':'+secondsStr); try { num_rounds = parseInt(variables['num_rounds']); } catch (err) { alert(err); } $("#i_rounds").html(num_rounds); // at least as many players as the game launched with if (total_players < numPlayers) { total_players = numPlayers; } try { network_types = variables['network_types'].split(","); } catch (err) { alert(err); } try { num_maps = parseInt(variables['num_maps']); } catch (err) { alert(err); } Math.seedrandom(seed); /* var cond = Math.floor((Math.random() * 7) + 1); if(cond == 1) // never map, never score { showTeamModulo = 0; showMap = false; showScore = false; } else if(cond == 2) // never map, always score { showTeamModulo = 1; showMap = false; showScore = true; } else if(cond == 3) // always map, never score { showTeamModulo = 1; showMap = true; showScore = false; } else if(cond == 4) // always map, always score { showTeamModulo = 1; showMap = true; showScore = true; } else if(cond == 5) // never map, score 3rd { showTeamModulo = 3; showMap = false; showScore = true; } else if(cond == 6) // map 3rd, never score { showTeamModulo = 3; showMap = true; showScore = false; } else if(cond == 7) // map 3rd, score 3rd { showTeamModulo = 3; showMap = true; showScore = true; } */ /*try { showTeamModulo = parseInt(variables['showTeamModulo']); } catch (err) { alert(err); } if (showTeamModulo < 0) { var info = Math.floor(Math.random()*3); switch (info) { case 0: showTeamModulo = 0; break; case 1: // case 2: showTeamModulo = 1; break; default: showTeamModulo = 3; break; } } try { showScore = parseInt(variables['showScore']); if (isNaN(showScore)) { throw "showScore not valid" // throw error } if (showScore < 0) { showScore = Math.floor(Math.random*2); // 0 or 1 } switch(showScore) { case 0: showScore = false; break; default: showScore = true; break; } } catch (err) { // alert(err); try { showScore = variables['showScore'].toLowerCase() == 'true'; } catch (err2) { showScore = true; // alert(err2); } } try { showMap = parseInt(variables['showMap']); if (isNaN(showMap)) { throw "showMap not valid" // throw error } if (showMap < 0) { showMap = Math.floor(Math.random*2); // 0 or 1 } switch(showMap) { case 0: showMap = false; break; default: showMap = true; break; } } catch (err) { // alert(err); try { showMap = variables['showMap'].toLowerCase() == 'true'; } catch (err2) { showMap = true; // alert(err2); } }*/ try { var randScale = parseFloat(variables['randScale']); // .7 means .7 to 1 var randRange = 1.0-randScale; scaleFactor = randScale + Math.random() * randRange; scale *= scaleFactor; // alert(scale+" "+scaleFactor); } catch (err) { } initializeNetwork(); initializeBots(); initializeHistory(); initializeGameBoard(); setInterval(advanceCountdowns, 100); if (!showScore) { setNeighborBarVisibility(false); } initializeInstructions(); // delme, for testing color // for (var i = 1; i <= num_rounds; i++) { // setBar(myid,i,Math.floor(max_score*i/num_rounds), i*10, i*10); // } // attempt to capture space/enter, but it doesn't work in raphael // $( "#canvas" ).keypress(function( event ) { // alert(event.which); // if ( event.which == 13 ) { // event.preventDefault(); // } // }); } var instructionPanel = 1; function initializeInstructions() { setRound(INSTRUCTION_ROUND); $('#instructions').modal({'show':true,'backdrop':"static"}).on('hidden.bs.modal', function (e) { instructionsComplete(); }); if (numPlayers > 1) { // don't start timer if single player setCountdown("instruction_time_limit",instruction_timeout); } $('#close_instructions').click(instructionNext); } function instructionNext() { switch(instructionPanel) { case 1: $('#instructions1').fadeOut(400, function() { $('#instructions2').fadeIn(400); }); // break; // case 2: $('#im_ready').hide(); $('#close_instructions').html("Play"); // $('#instructions2').fadeOut(400, function() { // $('#instructions3').fadeIn(400); // }); break; case 2: $('#instructions').modal('hide'); break; } instructionPanel++; } /** * called when instructions are closed (forcably or not) */ function instructionsComplete() { stopCountdown("instruction_time_limit"); submit("Instructions Complete"); if (currentRound == INSTRUCTION_ROUND) { // this check is only needed for reconnect waitForOtherPlayers(); } } function initializeGameBoard() { // alert(getFile("ground.jpg")); initializeMap(MAP_W, MAP_H, 0); buildMap((seed % num_maps)+1); $("#myCanvas").css("background-image","url('"+getFile("ground.jpg")+"')"); paper = Raphael("canvas", MAP_W*P, MAP_H*P); // paper.circle(256,256,256); $('#canvas').bind('click', mapClick); $('#drill').bind('click', drill); $('#x_coord').bind("keyup change",updateUserClick); $('#y_coord').bind("keyup change",updateUserClick); } /** * this is the network graph... who's wells you can see */ function initializeNetwork() { Math.seedrandom(seed); playerRadix = Math.floor(Math.random()*total_players); myNetworkId = getNetworkId(myid); network_type = network_types[Math.floor(Math.random()*network_types.length)]; $(".gameid").append(network_type+" "+showTeamModulo); // var s = "playerRadix:"+playerRadix+"\n"; // for (var i = 1; i < total_players; i++) { // var nid = getNetworkId(i); // s+= i + " => "+ nid + " => " + getPlayerId(nid) + "\n"; // } // alert(s); switch(total_players) { case 1: case 2: case 3: switch(network_type.toLowerCase()){ case 'clique': network=[[1,3], [1,2], [2,3]]; num_channels=3; break; case 'simplicial': network=[[1,3,4],[1,2,4], [2,3,4]]; num_channels=4; break; case 'hypergraph': network=[[1], [1], [1]]; num_channels=1; break; default: alert("error: invalid network"); } break; case 4: // fully connected for (var i = 1; i <= total_players; i++) { network[i] = []; for (var j = 1; j <= total_players; j++) { if (i != j) { network[i].push(j); } } } break; case 5: switch(network_type.toLowerCase()){ case 'line': network[1] = [2]; network[2] = [1,3]; network[3] = [2,4]; network[4] = [3,5]; network[5] = [4]; // network[1] = [2,3,4,5]; // network[2] = [1,3,4,5]; // network[3] = [1,2,4,5]; // network[4] = [1,2,3,5]; // network[5] = [1,2,3,4]; break; case 'clique': network[1] = [2,3,4,5]; network[2] = [1,3,4,5]; network[3] = [1,2,4,5]; network[4] = [1,2,3,5]; network[5] = [1,2,3,4]; // network[1] = [2,3,4,5]; // network[2] = [1,3,4,5]; // network[3] = [1,2,4,5]; // network[4] = [1,2,3,5]; // network[5] = [1,2,3,4]; break; case 'triangles': network[1] = [2,3]; network[2] = [1,3]; network[3] = [1,2,4,5]; network[4] = [3,5]; network[5] = [3,4]; // network[1] = [2,3,4,5]; // network[2] = [1,3,4,5]; // network[3] = [1,2,4,5]; // network[4] = [1,2,3,5]; // network[5] = [1,2,3,4]; break; case 'star': network[1] = [5]; network[2] = [5]; network[3] = [5]; network[4] = [5]; network[5] = [1,2,3,4]; // network[1] = [2,3,4,5]; // network[2] = [1,3,4,5]; // network[3] = [1,2,4,5]; // network[4] = [1,2,3,5]; // network[5] = [1,2,3,4]; break; } break; case 8: switch(network_type.toLowerCase()){ case 'connectedcliques': network[1] = [2,3,4]; network[2] = [1,3,4]; network[3] = [1,2,4]; network[4] = [1,2,3,5]; network[5] = [4,6,7,8]; network[6] = [5,7,8]; network[7] = [5,6,8]; network[8] = [5,6,7]; break; case 'connectedstars': network[1] = [4]; network[2] = [4]; network[3] = [4]; network[4] = [1,2,3,5]; network[5] = [4,6,7,8]; network[6] = [5]; network[7] = [5]; network[8] = [5]; break; } break; case 16: switch(network_type.toLowerCase()) { case 'a': network[1] = [ 2, 3, 4]; network[2] = [ 1, 3, 4]; network[3] = [ 1, 2, 5]; network[4] = [ 1, 2, 5]; network[5] = [ 3, 4, 6]; network[6] = [ 5, 7, 8]; network[7] = [ 6, 8, 9]; network[8] = [ 6, 7, 9]; network[9] = [ 7, 8,10]; network[10] = [ 9,11,12]; network[11] = [10,12,16]; network[12] = [10,11,13]; network[13] = [12,14,15]; network[14] = [13,15,16]; network[15] = [13,14,16]; network[16] = [11,14,15]; break; case 'b': network[1] = [ 2, 3, 4]; network[2] = [ 1, 3, 4]; network[3] = [ 1, 2, 5]; network[4] = [ 1, 2, 5]; network[5] = [ 3, 4, 6]; network[6] = [ 5, 7, 8]; network[7] = [ 6, 8, 9]; network[8] = [ 6, 7,10]; network[9] = [ 7,10,11]; network[10] = [ 8, 9,11]; network[11] = [ 9,10,12]; network[12] = [11,13,16]; network[13] = [12,14,15]; network[14] = [13,15,16]; network[15] = [13,14,16]; network[16] = [12,14,15]; break; case 'c': network[1] = [ 2, 3, 4]; network[2] = [ 1, 3, 4]; network[3] = [ 1, 2, 5]; network[4] = [ 1, 2, 5]; network[5] = [ 3, 4, 6]; network[6] = [ 5,11,16]; network[7] = [ 8, 9,10]; network[8] = [ 7, 9,10]; network[9] = [ 7, 8,11]; network[10] = [ 7, 8,11]; network[11] = [ 6, 9,10]; network[12] = [13,14,15]; network[13] = [12,14,15]; network[14] = [12,13,16]; network[15] = [12,13,16]; network[16] = [ 6,14,15]; break; case 'd': network[1] = [ 2, 3,16]; network[2] = [ 1, 3, 4]; network[3] = [ 1, 2, 4]; network[4] = [ 2, 3, 5]; network[5] = [ 4, 6, 7]; network[6] = [ 5, 7, 8]; network[7] = [ 5, 6, 8]; network[8] = [ 6, 7, 9]; network[9] = [ 8,10,11]; network[10] = [ 9,11,12]; network[11] = [ 9,10,12]; network[12] = [10,11,13]; network[13] = [12,14,15]; network[14] = [13,15,16]; network[15] = [13,14,16]; network[16] = [14,15, 1]; break; case 'e': network[1] = [ 2, 3, 6]; network[2] = [ 1, 5,10]; network[3] = [ 1, 4, 8]; network[4] = [ 3, 5, 7]; network[5] = [ 2, 4, 9]; network[6] = [ 1, 7,11]; network[7] = [ 4, 6,13]; network[8] = [ 3,12,16]; network[9] = [ 5,13,14]; network[10] = [ 2,12,15]; network[11] = [ 6,12,14]; network[12] = [ 8,10,11]; network[13] = [ 7, 9,15]; network[14] = [ 9,11,16]; network[15] = [10,13,16]; network[16] = [ 8,14,15]; break; case 'f': network[1] = [ 2, 3, 7]; network[2] = [ 1, 5, 6]; network[3] = [ 1, 4, 8]; network[4] = [ 3, 7, 9]; network[5] = [ 2, 8,10]; network[6] = [ 2, 9,10]; network[7] = [ 1, 4,11]; network[8] = [ 3, 5,11]; network[9] = [ 4, 6,12]; network[10] = [ 5, 6,14]; network[11] = [ 7, 8,13]; network[12] = [ 9,13,16]; network[13] = [11,12,15]; network[14] = [10,15,16]; network[15] = [13,14,16]; network[16] = [12,14,15]; break; case 'g': network[1] = [ 2, 3, 5]; network[2] = [ 1, 4, 8]; network[3] = [ 1, 6, 9]; network[4] = [ 2, 5,11]; network[5] = [ 1, 4,12]; network[6] = [ 3, 7,10]; network[7] = [ 6, 8, 9]; network[8] = [ 2, 7,15]; network[9] = [ 3, 7,13]; network[10] = [ 6,11,14]; network[11] = [ 4,10,13]; network[12] = [ 5,14,15]; network[13] = [ 9,11,16]; network[14] = [10,12,16]; network[15] = [ 8,12,16]; network[16] = [13,14,15]; break; case 'h': network[1] = [ 2, 3, 5]; network[2] = [ 1, 3, 4]; network[3] = [ 1, 2, 4]; network[4] = [ 2, 3, 7]; network[5] = [ 1, 6, 8]; network[6] = [ 5, 9,11]; network[7] = [ 4, 9,10]; network[8] = [ 5,10,12]; network[9] = [ 6, 7,12]; network[10] = [ 7, 8,11]; network[11] = [ 6,10,15]; network[12] = [ 8, 9,13]; network[13] = [12,14,16]; network[14] = [13,15,16]; network[15] = [11,14,16]; network[16] = [13,14,15]; break; } break; default: alert("error: invalid number of players"); } } function getNetworkId(playerId) { return 1+( (playerId+playerRadix-1) % total_players); } function getPlayerId(networkId) { return 1+( (networkId-1-playerRadix+total_players) % total_players); } function initializeHistory() { addHistoryPanel(myNetworkId, "Player "+myNetworkId+" (You)"); //for (var i = 0; i < Math.min(3,network[myNetworkId].length); i++) { for (var i = 0; i < network[myNetworkId].length; i++) { var myBuddy = getPlayerId(network[myNetworkId][i]); myNetwork.push(myBuddy); addHistoryPanel(network[myNetworkId][i], "Player "+network[myNetworkId][i]); } var initialization = {'networkType':network_type, 'myNetworkId':myNetworkId, 'myNeighbors':myNetwork, 'teamModulo':showTeamModulo, 'showScore':showScore, 'showMap':showMap, 'scaleFactor':scaleFactor}; for (var i in botBehavior) { initialization['bot'+i] = botBehavior[i]['name']; } submit(JSON.stringify(initialization)); // initializeGameRound(1); // if starts under instructions } var panelCtr = 0; function addHistoryPanel(networkId, name) { // it's networkId, not playerId // first 3 have the bottom dashed panelCtr++; var extraClass = " bottom_dashed"; if (panelCtr == 5) { extraClass = ""; } var s = '
'+ '
'+name+'
'+ '
'+ '
Score
'+ ''; for (var i = 1; i <= num_rounds; i++) { // s+=''; } s+=''; for (var i = 1; i <= num_rounds; i++) { s+=''; } s+= ''; for (var i = 1; i <= num_rounds; i++) { s+=''; } s+= '
?
'+i+'
'+ '
Round
'+ '
?
'+ '
'; //alert(s); $("#history_wrapper").append(s); } function getScore(x,y) { return Math.min(max_score,Math.max(0,Math.floor(scale*map[x][y]))); } var userClick = null; function updateUserClick() { if (submitted) return; fail = false; var x = $("#x_coord").val(); if (x.length > 0) { x = parseInt(x); if (isNaN(x) || x < 0 || x > MAP_W) { $("#x_coord").val(""); fail = true; } } var y = $("#y_coord").val(); if (y.length > 0) { y = parseInt(y); if (isNaN(y) || y < 0 || y > MAP_H) { $("#y_coord").val(""); fail = true; } } if (fail) { if (userClick != null) { userClick.remove(); userClick = null; } } if (userClick == null) { userClick = paper.rect(x-1,y-1,3,3); userClick.attr({fill: click_color, stroke: click_color}); } else { userClick.attr({x: x-1, y: y-1}); } } var round = 1; function mapClick(evt) { if (submitted) return; var offset = $(this).offset(); var x = Math.floor(evt.pageX-offset.left); var y = Math.floor(evt.pageY-offset.top); $("#x_coord").val(x); $("#y_coord").val(y); updateUserClick(); } function drill() { if (currentRound < FIRST_ACTUAL_ROUND) return; // waiting for instructions var x = $("#x_coord").val(); var y = $("#y_coord").val(); if (x.length == 0 || y.length == 0) { alert("Please click a new well site on the map."); return; } x = parseInt(x); y = parseInt(y); makeChoice(x,y); } function resetBar(networkId, round) { var bar = $("#bar_" + networkId + "_" + round); bar.addClass('pre_round'); bar.html("?"); bar.removeClass('skip_round'); var color = "rgb(255,255,255)"; bar.css("background-color", color); bar.css("height", "85%"); } function setBar(networkId,round,value, x, y) { var bar = $("#bar_"+networkId+"_"+round) bar.removeClass('pre_round'); if (value < 0) { bar.addClass('skip_round'); bar.html("X"); return; } var fraction = Math.min(1.0,Math.max(0,value/max_score)); // 0-1 var height = Math.floor(100*fraction); $("#mapValue").html(height); var midpoint = 0.4; // where yellow is; lower for more red on the chart, higher for more blue on the chart var r = 255; var g = 0; var b = 0; if (fraction < midpoint) { // blue => yellow r = g = Math.floor(255.0 * fraction/midpoint); b = 255-r; } else { // yellow => red r = 255; g = 255 - Math.floor(255* (fraction-midpoint)/(1.0-midpoint) ); //b = 0; // from above } var color = "rgb("+r+","+g+","+b+")"; bar.html(""); bar.css("background-color",color); bar.css("height",height+"%"); bar.attr("title",value); if (showMap || networkId == myNetworkId) { bar.click(function() { $("#x_coord").val(x); $("#y_coord").val(y); updateUserClick(); }); } if (x >= 0) { var point = paper.rect(x-1,y-1,3,3); point.realColor = color; point.realValue = value; if (networkId != myNetworkId) { neighborPoints.push(point); if (!showScore) { color = click_color; value = "?"; point.toBack(); // so black ones don't cover colored ones especially if copy } } else { yourPoints.push(point); //Stores currentplayer point refrences } point.attr({fill: color, stroke: color, title: value}); } } var gameRound = 0; var submissions = {}; // round => networkId => [x,y,val]; keep track of who submitted for bot behavior function initializeGameRound(newGameRound) { submitted = false; if (userClick != null) { userClick.remove(); userClick = null; } gameRound = newGameRound; setRound(gameRound+FIRST_ACTUAL_ROUND-1); submissions[currentRound] = {}; $("#x_coord").val(""); $("#y_coord").val(""); var seconds = round_seconds; // no timer on round one if single player setCountdown("timer",seconds); $("#round").html(gameRound); $("#score").html(numberWithCommas(total_score)); doneWaitingForOtherPlayers(); } function numberWithCommas(x) { return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } //function countdownUpdate(id,diff,clockString) { // $("#instruction_time_limit").html(clockString); //} function countdownExpired(id) { if (id=="instruction_time_limit") { $('#instructions').modal('hide'); // calls instructionsComplete() return; } var x = $("#x_coord").val(); var y = $("#y_coord").val(); if (x.length == 0 || y.length == 0 || !checkDistance(myid,x,y)) { x = -1; y = -1; // TODO: notify user } x = parseInt(x); y = parseInt(y); submitMyChoice(x,y); submitRemainingBots(); } function completeRound() { // draw the bar/points stopCountdown("timer"); if (drilling_history == 1) { if (gameRound > 1) removeOldInformation(); } // mine var neighbor = myNetworkId; var val = submissions[currentRound][neighbor]; if (val[2] > 0) { total_score+=val[2]; } setBar(neighbor,gameRound,val[2],val[0],val[1]); // log("rendering neighbors gameRound:"+gameRound+" currentRound:"+currentRound); for (var i = 0; i < network[myNetworkId].length; i++) { neighbor = network[myNetworkId][i]; val = submissions[currentRound][neighbor]; setBar(neighbor,gameRound,val[2],val[0],val[1]); } // Set the visible network for this particular round. var options=[]; for (var i = 1;i 0) { var temp = neighborPoints.pop(); temp.hide(); } //Remove your previous information while (yourPoints.length > 0) { var temp = yourPoints.pop(); temp.hide(); } } function showSurvey() { setRound(300); $('#survey1').modal({'show':true,'backdrop':"static"}); } //called when done with survey question function q1(val) { submit(val); $('#survey1').modal('hide'); endGame(); } function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } /** * the points on the map * @param enable */ function setNeighborPointVisibility(enable) { for (var p in neighborPoints) { if (enable) { neighborPoints[p].show(); } else { neighborPoints[p].hide(); } } } function setNeighborBarVisibility(enable) { var options=[]; for (var i = 1;i 0) { //if round score was not 0 and is the current player valids++; } } var pay = false; if (valids>=num_rounds*.699){ //if participated in at least 69.9% of rounds payAMT(true,0.0); }else{ payAMT(false,0.0); } } //CODE THAT CURRENTLY ENDS WILDCAT WELLS function endGame() { //properly color them all in the map for (p in neighborPoints) { var point = neighborPoints[p]; point.attr({fill: point.realColor, stroke: point.realColor, title: point.realValue}); } //setNeighborBarVisibility(true); //setNeighborPointVisibility(true); //revealMap(); //CR: Don't reveal map and scores at the end of the game setNeighborBarVisibility(false); setNeighborPointVisibility(false); //revealMap(); doneWaitingForOtherPlayers(); //CR: This shows the final popup (but we can remove the scores in the popu) // Or not show the popup at all, it doesn't really do anything anyway // showScores(); //this shows the score and end game popup //experimentComplete(); if (IS_AMT==true){ amtScoreFilter(); }else{ experimentComplete(); } //payAMT(pay,0.0); // alert("Congratulations! You collected "+numberWithCommas(total_score)+" barrels of oil."); } // player's choice, return false if too close function makeChoice(x,y) { if (!checkDistance(myid,x,y)) { alert("Too close to an existing well!"); return; } submitMyChoice(x,y); submitRemainingBots(); } function playerDisconnect(playerId) { log("Player Disconnect "+playerId); if (playerId < myid || currentRound == INSTRUCTION_ROUND) { submitRemainingBots(); } } /** * return false if illegal for this player * note that we only check against the player's network */ function checkDistance(playerId, x, y) { // if (gameRound == 1) return true; // first round return true; // abandon this feature var networkId = getNetworkId(playerId); for (var round = FIRST_ACTUAL_ROUND; round < currentRound; round++) { var sub = submissions[round][networkId]; // self if (sub[2] >= 0) { // there's a valid submission var dx = x-sub[0]; var dy = y-sub[1]; var d2 = dx*dx + dy*dy; if (d2 < min_distance2) { return false; } } for (var i = 0; i < network[myNetworkId].length; i++) { sub = submissions[round][network[networkId][i]]; // neighbor if (sub[2] >= 0) { // there's a valid submission var dx = x-sub[0]; var dy = y-sub[1]; var d2 = dx*dx + dy*dy; if (d2 < min_distance2) { return false; } } } } return true; } var submitted = false; function submitMyChoice(x,y) { if (submitted) return; waitForOtherPlayers(); submitChoice(myid,x,y); } function waitForOtherPlayers() { submitted = true; $("#drill").val("Waiting for other players."); $("#waitForOthers").fadeIn(); } function doneWaitingForOtherPlayers() { $("#drill").val("Drill!"); $("#waitForOthers").fadeOut(); } function showScores() { var scores = [[0,0]]; // player 0 for (var playerId = 1; playerId <= total_players; playerId++) { scores.push([0,getNetworkId(playerId)]); // initialize array for (var round = FIRST_ACTUAL_ROUND; round < FIRST_ACTUAL_ROUND+num_rounds; round++) { var val = submissions[round][getNetworkId(playerId)][2]; // the score part of the array if (val > 0) { scores[playerId][0]+=val; } } } scores.splice(0,1); // remove first element scores.sort(function(a,b) { return b[0] - a[0]; }); var lastRank = 1; var last = 0; for (var idx in scores) { var rank = parseInt(idx)+1; var playerName = "Player "+scores[idx][1]; if (scores[idx][1] == myNetworkId) { playerName+= " (You)"; } var scoreString = numberWithCommas(scores[idx][0]); if (last==scores[idx][0]) { rank = lastRank; } else { last=scores[idx][0]; lastRank = rank; } //CR: trying to not show score $("#scoreTable").append('#'+rank+''+playerName+''+scoreString+''); } $("#scores").fadeIn(); } function hideScores() { $("#scores").fadeOut(); } function submitChoice(playerId,x,y) { var score = -1; try { score = getScore(x,y); } catch (err) { x = -1; y = -1; } submitBot(playerId, currentRound, JSON.stringify([x,y,score])); } /** * * * @param jsonString [['explore', 3],[ 'exploit',5],[ 'random', 2],['explore',8]] * @returns // round => array of functions to call for the bot's behavior */ function initializeBotOrder(jsonString, name) { var ret = []; try { var orders = JSON.parse(jsonString); for (var c in orders) { for (var i = 0; i < orders[c][1]; i++) { if (orders[c][0] == "explore") { ret.push(botExplore); } else if (orders[c][0] == "random") { ret.push(botRandom); } else if (orders[c][0] == "exploit") { ret.push(botExploit); } else if (orders[c][0] == "copy") { ret.push(botCopy); } } } } catch (err) { alert("Error parsing "+jsonString+"\n"+err); } // repeat the last move until the end -- this is in case the instructions are shorter than the number of rounds while (ret.length < num_rounds) { ret.push(ret[ret.length-1]); } ret["name"] = name; return ret; } var bot_peakLimit = 5; var bot_random_rule = "50,25,25"; // explore, exploit, copy; converted to array of numbers var bot_random_rule_sum = 100; var bot_exploit_near = 10; var botExploitLocation = "random"; var bot_copy_rule = "random"; function initializeBots() { var clusterBehavior = initializeBotOrder(variables['cluster'],'cluster'); var humanBehavior = initializeBotOrder(variables['human'],'human'); var num_clusters_remaining = parseInt(variables['num_cluster_bots']); try { bot_peakLimit = parseInt(variables['peakLimit']) * scaleFactor; } catch (err) { alert(err); } try { bot_random_rule = variables['random_rule']; } catch (err) { alert(err); } bot_random_rule = bot_random_rule.split(",").map( function(val) { return parseInt(val); }); // string of numbers to array of numbers bot_random_rule_sum = bot_random_rule.reduce( function(total, num){ return total + num }, 0); try { bot_exploit_near = parseInt(variables['near']); } catch (err) { alert(err); } // assume random exploitation location var x = Math.floor(Math.random()*MAP_W); var y = Math.floor(Math.random()*MAP_H); botExploitLocation = [x,y]; var foo = variables['exploit_location'].split(","); if (foo.length == 2) { botExploitLocation = foo.map( function(val) { return parseInt(val); }); } try { bot_copy_rule = variables['copy_rule']; } catch (err) { alert(err); } for (var i = 1; i <= total_players; i++) { var playerId = i; //getPlayerId(i); botBehavior[playerId] = humanBehavior; // human dropouts, and default bots // cluster bots get different behavior if ( (i > numPlayers) && (num_clusters_remaining > 0) ) { log("player "+getNetworkId(playerId)+"("+playerId+") is a clusterBot"); botBehavior[playerId] = clusterBehavior; num_clusters_remaining--; } } } function submitRemainingBots() { // abort if I'm not the lowest active player for (var i = 1; i < myid; i++) { if (activePlayers[i]) return; } log('submitRemainingBots'); if (currentRound == INSTRUCTION_ROUND) { for (var i = 1; i <= numPlayers; i++) { if (!activePlayers[i]) { log('submit Player Dropped '+i); submitBot(i, currentRound, "Player Dropped"); } } return; } // submit anyone who needs it for (var i = 1; i <= total_players; i++) { // log(i+" in "+JSON.stringify(submissions[currentRound])); if (!(String.valueOf(i) in submissions[currentRound])) { if (i > numPlayers || !activePlayers[i]) { // bot from beginning || dropped player doBotBehavior(i); } } } } function doBotBehavior(playerId) { log('doBotBehavior:'+playerId); var bestSub = null; // x,y,val var myNet = network[getNetworkId(playerId)].slice(0); myNet.push(getNetworkId(playerId)); for (var round_idx = FIRST_ACTUAL_ROUND; round_idx < currentRound; round_idx++) { for (neighbor_idx in myNet) { var sub = submissions[round_idx][myNet[neighbor_idx]]; if ((bestSub == null) || (sub[2] > bestSub[2])) { bestSub = sub; } } } // hard coded grab for good spot if (bestSub != null && bestSub[2] >= bot_peakLimit) { botCopy(playerId,bestSub); return; } botBehavior[playerId][currentRound-FIRST_ACTUAL_ROUND](playerId,bestSub); } function botExplore(playerId, bestSub) { log('botExplore:'+playerId); var x = Math.floor(Math.random()*MAP_W); var y = Math.floor(Math.random()*MAP_H); submitChoice(playerId,x,y); } function botCopy(playerId, bestSub) { log('botCopy:'+playerId); if (bestSub != null) { botCopyHelper(playerId, bestSub, bot_copy_rule); } else { botExplore(playerId, bestSub); } } var bot_copy_options = ["highest","any","users_last"]; function botCopyHelper(playerId, bestSub, copyType) { log('botCopyHelper:'+playerId+","+copyType); if (copyType == "random") { botCopyHelper(playerId, bestSub, bot_copy_options[Math.floor(Math.random()*bot_copy_options.length)]); return; } if (copyType == "highest") { submitChoice(playerId,bestSub[0],bestSub[1]); return; } if (copyType == "any") { var options = []; var myNet = network[getNetworkId(playerId)].slice(0); for (var round_idx = FIRST_ACTUAL_ROUND; round_idx < currentRound; round_idx++) { for (neighbor_idx in myNet) { var sub = submissions[round_idx][myNet[neighbor_idx]]; if (sub[2] > 0) { options.push(sub); } } } if (options.length < 1) { botExplore(playerId,bestSub); return; } var bestSub = options[Math.floor(Math.random()*options.length)]; submitChoice(playerId,bestSub[0],bestSub[1]); return; } if (copyType == "users_last") { var options = []; var myNet = network[getNetworkId(playerId)].slice(0); for (neighbor_idx in myNet) { var otherPid = getPlayerId(myNet[neighbor_idx]); if (otherPid < numPlayers) { var sub = submissions[currentRound][myNet[neighbor_idx]]; options.push(); } } if (options.length < 1) { botExplore(playerId,bestSub); return; } var bestSub = options[Math.floor(Math.random()*options.length)]; submitChoice(playerId,bestSub[0],bestSub[1]); return; } // default behavior botExplore(playerId, bestSub); } function botExploit(playerId, bestSub) { // log('botExploit:'+playerId+" "+botExploitLocation); // if (bestSub != null) { var direction = Math.random()*Math.PI*2.0; var distance = Math.random()*bot_exploit_near; var x = Math.floor(botExploitLocation[0]+Math.cos(direction)*distance); var y = Math.floor(botExploitLocation[1]+Math.sin(direction)*distance); x = Math.min(MAP_W, Math.max(0,x)); // bounds check y = Math.min(MAP_H, Math.max(0,y)); // bounds check log('botExploit:'+playerId+" "+botExploitLocation+" direction:"+direction+" ("+x+","+y+")"); submitChoice(playerId,x,y); // } else { // botExplore(playerId, bestSub); // } } function botRandom(playerId, bestSub) { log('botRandom:'+playerId); var roll = Math.floor(Math.random()*bot_random_rule_sum); var choice = 0; var choiceSum = 0; for (choice = 0; choice < bot_random_rule.length; choice++) { choiceSum+=bot_random_rule[choice]; if (roll < choiceSum) { break; } } switch(choice) { case 0: botExplore(playerId, bestSub); break; case 1: botExploit(playerId, bestSub); break; default: botCopy(playerId, bestSub); break; } } function newMove(playerId, idx, round) { // wait for everyone to close the instructions if (round == INSTRUCTION_ROUND) { for (var i = 1; i <= numPlayers; i++) { if (moves[i] < 1) return; } initializeGameRound(1); return; } fetchMove(playerId, round, idx, function(val, participant, round, index) { submissions[round][getNetworkId(playerId)] = JSON.parse(val); for (var i = 1; i <= total_players; i++) { if (!(i in submissions[currentRound])) { return; } } completeRound(); }); }