Published
Edited
Apr 28, 2019
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function runPADGMultipleTimes(LL,evnts,grps,updateFun) {
let arrangement = PADGopt(LL,evnts,grps,grps,updateFun)
//console.log('initial arrangement')
//console.log(checkArrangement(arrangement, groups, events))
let happiness = totalHappiness(arrangement,groups)/groups.length
let grp = grps.slice()
let numOptRounds = 0
for(let round = 1; round < numOptRounds; round++) {
//find groups that were not set to any event
let groupsNotAssigned = arrangement.filter(ele => ele.assignment === -1)
let newGroups = []
for(let g in groupsNotAssigned){
newGroups.push(groups.filter(ele => ele.name === groupsNotAssigned[g].name)[0])
}

//update new events so that the groups already assigned to them are included
let newEvents = evnts.map(ele => {
return {
name: ele.name,
min: ele.min,
max: ele.max,
}
})

newEvents.playerCount = (eventName) => {
// function to count how many players are already assigned to an event
let e = newEvents.findIndex(ele => {
return ele.name === eventName })
if(typeof(newEvents[e].groups) !== 'undefined' && newEvents[e].groups.length > 0) {

return newEvents[e].groups.reduce((a, b) => {
return a+grp.filter(g=>g.name === b)[0].size
}, 0);
} else {
return 0
}

}

for(let e in newEvents) {
let playersInThisEvent = arrangement.filter(ele=>ele.assignment === Number(e))
.map(ele => ele.name)
if(playersInThisEvent.length>0) {
newEvents[e].groups = playersInThisEvent
}
}
//construct new L which includes only gain from assigning groups in newGroups
let newL = []
for(let i=0;i<newGroups.length;i++) {
for(let j=0;j<evnts.length;j++) {
let ind = newGroups[i].pref.indexOf(j)
let gain = 0
if(ind!== -1) {
gain = alpha*1/(ind+1)
if(groups[i].pref.length>1) {
gain = gain*(1/groups[i].pref.length)
}
}
newL.push({
name: newGroups[i].name,
event: j,
size: newGroups[i].size,
gain: gain
})
}
}
newL = newL.sort((a,b) => {
if(a.gain >= b.gain) {
return 1;
} else {
return -1
}

})
let newArrangement = PADGopt(newL,newEvents,newGroups,grps,updateFun)
newArrangement.map(ele => {
if(ele.assignment !== -1) {
let arrInd = arrangement.findIndex(arrEle => arrEle.name === ele.name)
arrangement[arrInd].assignment = ele.assignment

}
})
}
return arrangement
}
Insert cell
updateL = (LL,grp,gInd) => {
/*Updates the list LL so that gain increases as possibilities for
assignment decrease */
LL.forEach(ele => {
if(ele.name===grp[gInd].name ) {
//calculate the number of preferences
let numPref = grp[gInd].pref.length
let numPossibilities = LL.filter(g=>g.gain > 0).filter(g => g.name ===ele.name).length
if(numPossibilities < numPref && numPossibilities > 1) {
ele.gain = ele.gain + (1.0-alpha)/(numPossibilities-1)*(numPref-numPossibilities)/(3-1)
}
}
})
return LL.sort((a,b) => {
if(a.gain >= b.gain) {
return 1;
} else {
return -1
}
})
}
Insert cell
PADGopt = (Lis,evnt,grp,grpAll,updateFun) => {
let LL = JSON.parse(JSON.stringify(Lis))
let evn = JSON.parse(JSON.stringify(evnt))
let M = grp.map(e => {
return {name: e.name,assignment: -1}
})

let S = evn.map(ele => {
return {
name: ele.name,
min: ele.min,
max: ele.max,
groups: ele.hasOwnProperty('groups') ? ele.groups : []
}
})
// function definitions for S
if(typeof(S.playerCount) === 'undefined') {
S.playerCount = (e) => {
// function to count how many players are already assigned to an event

if(S[e].groups.length > 0) {
return S[e].groups.reduce((a, b) => {
return a+grpAll.filter(g=>g.name === b)[0].size
}, 0);
} else {
return 0
}

}
}

let P = [] // Phantom events
// function definitions for P
P.createEntry = (newEntry) => {
//function to create a new entry to S
P.push({name: newEntry.name,
min: newEntry.min,
max: newEntry.max,
})
}

P.removeEntry = (eventName) => {
let ind = P.findIndex(ele => ele.name === eventName)

P.splice(ind,1)
}

P.includes = (eventName) => {
//check if event named eventName is in P
if(P.filter(ele => ele.name === eventName).length === 1) {
return 1
} else {
return 0
}
}

//define deficit

let deficit = 0

//initialize V to be the same as groups
let V = grp.slice()

//MAIN LOOP STARTS HERE

LL = LL.filter(ele => ele.gain > 0)
while(LL.length>0) {
let u = LL.pop() // u is the last index of LL
// check if there are enough people signed up for this game
// in order to avoid matching some group with this event
// without hope of this event ever happening
let numPlayersToThisEvent = 0
grp.filter(ele => {

return typeof(ele.pref.find(p => p===u.event)) !== 'undefined'})
.forEach(ele => {
numPlayersToThisEvent = numPlayersToThisEvent + ele.size
})
if(u.gain === 0 || numPlayersToThisEvent < evnt[u.event].min) {
// consider only cases where adding u to event increases happiness
// and those where there is even theoretically possible to have
// minimum number of players
continue
}

let Mind = M.findIndex(g => g.name === u.name)

if(M[Mind].assignment === -1 && (S.playerCount(u.event) + u.size) <= S[u.event].max) {

// user u is not assigned and there is space in the event where we try to place u
//console.log('before')
//console.log(S.playerCount(u.event))
let playersBefore = S.playerCount(u.event)
if(S.playerCount(u.event) === 0) {
//no players in S or S is not yet a real event
if( deficit + (evnt[u.event].min - u.size) < V.length) {
// adding u to this event does not decrease deficit over critical size
// since event is not in S add it to P

// add to deficit how much space was left over in this event
deficit = deficit + (evnt[u.event].min - u.size)
if(P.includes(S[u.event].name) === 0) {
let newPEntry = {
name: evnt[u.event].name,
min: evnt[u.event].min,
max: evnt[u.event].max,
}
P.createEntry(newPEntry)
}
} else {
continue
}

} else {
// S has players and is happening anyway
if(P.includes(evnt[u.event].name) === 1) {
deficit = deficit - u.size
}

}

S[u.event].groups.push(u.name)


V = V.filter(ele => ele.name !== u.name)
if(S.playerCount(u.event) >= S[u.event].min && P.includes(S[u.event].name) === 0) {
// this event is not a phantom event, set M(u) to a
M[Mind].assignment = u.event
// remove u from other events
S.forEach(ele => {
if(ele.name !== S[u.event].name) {
let nPlayersBefore = S.playerCount(S.findIndex(s=>s.name === ele.name))
ele.groups = ele.groups.filter(e => e !== u.name)
// check that there are enough players in this event
let nPlayersAfter = S.playerCount(S.findIndex(s=>s.name === ele.name))
//if nPlayers < ele.min add this event as a phantom event
if(nPlayersAfter < nPlayersBefore && nPlayersAfter < ele.min) {
if(P.includes(ele.name) === 0) {
P.createEntry({
name: ele.name,
min: ele.min,
max: ele.max
})
}
// remove assignment

ele.groups.forEach(e => {

let MindTemp = M.findIndex(m => m.name === e)

if(M[MindTemp].assignment === S.findIndex(s=>s.name === ele.name)) {
M[MindTemp].assignment = -1;
}
})
}
}
})
}

if(S.playerCount(u.event) >= S[u.event].min && P.includes(S[u.event].name) === 1) {
// this event is a phantom event but has now enough players to be a real event
//console.log(u.event)
P.removeEntry(S[u.event].name) // remove this event from phantom events

S[u.event].groups.forEach(el => {
let MindTemp = M.findIndex(m => m.name === el)
M[MindTemp].assignment = u.event
})
for(let g in S[u.event].groups) {
S.forEach(ele => {
if(ele.name !== S[u.event].name) {
let nPlayersBefore = S.playerCount(S.findIndex(s=>s.name === ele.name))
ele.groups = ele.groups.filter(e => e !== S[u.event].groups[g])

// check that there are enough players in this event

let nPlayersAfter = S.playerCount(S.findIndex(s=>s.name === ele.name))
//if nPlayers < ele.min add this event as a phantom event
if(nPlayersAfter < nPlayersBefore && nPlayersAfter < ele.min) {
if(P.includes(ele.name) === 0) {
P.createEntry({
name: ele.name,
min: ele.min,
max: ele.max

})
}
// remove assignment if there is assignment to this event that is now phantom event

ele.groups.forEach(e => {

let MindTemp = M.findIndex(m => m.name === e)

if(M[MindTemp].assignment === S.findIndex(s=>s.name === ele.name)) {
M[MindTemp].assignment = -1;
}
})
}
}
})

}

}

//Update list LL if there was no assignment

if(M[Mind].assignment === -1) {

let gInd = grp.findIndex(g => g.name === u.name)
LL = updateFun(LL,grp,gInd)


}
}
}

return M

}
Insert cell
Insert cell
Insert cell
Insert cell
L = {
let L = []
for(let i=0;i<groups.length;i++) {
for(let j=0;j<events.length;j++) {
let ind = groups[i].pref.indexOf(j)
let gain = 0
if(ind!== -1) {
gain = alpha*1/(ind+1)
if(groups[i].pref.length>1) {
gain = gain*(1/groups[i].pref.length)
}
}
L.push({
name: groups[i].name,
event: j,
size: groups[i].size,
gain: gain
})
}
}
return L.filter(ele=>ele.gain>0).sort((a,b) => {
if(a.gain >= b.gain) {
return 1;
} else {
return -1
}
})
}
Insert cell
Insert cell
events = {
return games.filter(ele => ele.startTime === selectedGameTime)
.map(ele => {
return {
name: ele.title,
id: ele.id,
min: Number(ele.minAttendance),
max: Number(ele.maxAttendance)
}
})
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more