Added player chance calculations. In the middle of stream, changing computers.
This commit is contained in:
parent
2ea56d05c5
commit
242fbf2e56
@ -1,6 +1,6 @@
|
|||||||
#Sun Jul 04 00:39:50 CDT 2010
|
#Mon Jul 05 23:10:39 CDT 2010
|
||||||
app.version=0.3.0
|
app.version=0.3.0
|
||||||
|
build.number=47
|
||||||
src.dir=src
|
src.dir=src
|
||||||
build.number=4
|
|
||||||
build.dir=build
|
build.dir=build
|
||||||
app.name=team-maker
|
app.name=team-maker
|
||||||
|
86
src/com/jdbernard/teammaker/OddsCalculator.groovy
Normal file
86
src/com/jdbernard/teammaker/OddsCalculator.groovy
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
package com.jdbernard.teammaker
|
||||||
|
|
||||||
|
public class OddsCalculator {
|
||||||
|
|
||||||
|
Map odds = [:]
|
||||||
|
|
||||||
|
public void recalculate(List players, int spotsOpen, PlayerChooser chooser) {
|
||||||
|
|
||||||
|
// reset computed player odds
|
||||||
|
odds = [:]
|
||||||
|
|
||||||
|
/* this list will contain each possible selection path with
|
||||||
|
* the odds at each node. It is a list of lists of lists::
|
||||||
|
*
|
||||||
|
* oddsList: [
|
||||||
|
* path: [
|
||||||
|
* node: [player, odds]
|
||||||
|
* node: [player, odds]
|
||||||
|
* ]
|
||||||
|
* path: [
|
||||||
|
* node: [player, odds]
|
||||||
|
* node: [player, odds]
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
|
||||||
|
def oddsList = buildOdds([], players, spotsOpen)
|
||||||
|
|
||||||
|
/* The oddsMap is a condensed version of the oddsList, calculating
|
||||||
|
* the overall probability of each path::
|
||||||
|
*
|
||||||
|
* oddsMap: [
|
||||||
|
* [player, player]: pathOdds,
|
||||||
|
* [player, player]: pathOdds
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
|
def oddsMap = [:]
|
||||||
|
|
||||||
|
// calculate the overall odds for each path
|
||||||
|
oddsList.each { path ->
|
||||||
|
def pathPlayers = []
|
||||||
|
float pathOdds = 1f
|
||||||
|
|
||||||
|
// the overall odds of the path is the product of the odds for
|
||||||
|
// each node in the path
|
||||||
|
path.each { node ->
|
||||||
|
pathPlayers << node[0] // player
|
||||||
|
pathOdds *= node[1] // node odds
|
||||||
|
}
|
||||||
|
|
||||||
|
oddsMap[(pathPlayers)] = pathOdds
|
||||||
|
}
|
||||||
|
|
||||||
|
// now we calculate the overall odds for each player. The odds for a
|
||||||
|
// player is the sum of the odds of each path that contains that player
|
||||||
|
// (since that means he was picked for one of the nodes, ie., open
|
||||||
|
// spots)
|
||||||
|
players.each { player ->
|
||||||
|
float playerOdds = 0f
|
||||||
|
|
||||||
|
// check each path in the map
|
||||||
|
oddsMap.each { pathPlayers, pathOdds ->
|
||||||
|
// if we are in this path, add that probability to our own
|
||||||
|
if (pathPlayers.contains(player))
|
||||||
|
playerOdds += pathOdds
|
||||||
|
}
|
||||||
|
|
||||||
|
odds[(player)] = playerOdds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List buildOdds(List chosenPath, List players, int spotsOpen) {
|
||||||
|
def oddsList = []
|
||||||
|
|
||||||
|
if (spotsOpen == 0) return [chosenPath]
|
||||||
|
|
||||||
|
players.each { player ->
|
||||||
|
def odds = chooser.getOdds(players, player)
|
||||||
|
def newPath = chosenPath.clone()
|
||||||
|
newPath << [player, odds]
|
||||||
|
oddsList.addAll(buildOdds(newPath, players - [player], spotsOpen - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return oddsList
|
||||||
|
}
|
||||||
|
}
|
@ -1,14 +1,13 @@
|
|||||||
package com.jdbernard.teammaker
|
package com.jdbernard.teammaker
|
||||||
|
|
||||||
public abstract class PlayerChooser {
|
public abstract class PlayerChooser {
|
||||||
|
|
||||||
|
protected static final Random rand = new Random(System.currentTimeMillis())
|
||||||
|
|
||||||
public abstract Player choose (def players)
|
public abstract Player choose (def players)
|
||||||
public abstract float calculateOdds(def players, def player)
|
public abstract float getOdds(def players, Player player)
|
||||||
|
|
||||||
public final static Random = new Random(System.currentTimeMillis())
|
protected Player chooseRandomly(List choices) {
|
||||||
|
|
||||||
public static final Player chooseRandomly(def choices) {
|
|
||||||
choices.sort { rand.nextInt() }
|
choices.sort { rand.nextInt() }
|
||||||
return choices[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,33 +1,35 @@
|
|||||||
package com.jdbernard.teammaker
|
package com.jdbernard.teammaker
|
||||||
|
|
||||||
|
import java.awt.BorderLayout
|
||||||
import java.awt.Color
|
import java.awt.Color
|
||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import javax.swing.EmptyBorder
|
|
||||||
import javax.swing.JLabel
|
import javax.swing.JLabel
|
||||||
import javax.swing.JList
|
import javax.swing.JList
|
||||||
import javax.swing.JPanel
|
import javax.swing.JPanel
|
||||||
import javax.swing.LineBorder
|
|
||||||
import javax.swing.ListCellRenderer
|
import javax.swing.ListCellRenderer
|
||||||
|
import javax.swing.BorderFactory
|
||||||
|
|
||||||
public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
|
public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
|
||||||
|
|
||||||
boolean showStats
|
boolean showStats
|
||||||
boolean colored
|
boolean colored
|
||||||
|
|
||||||
private PlayerChooser chooser
|
private TeamMaker teamMaker
|
||||||
|
|
||||||
private JLabel nameLabel
|
private JLabel nameLabel
|
||||||
private JLabel statsLabel
|
private JLabel statsLabel
|
||||||
private static def linedBorder = new LineBorder(Color.BLACK)
|
|
||||||
private static def emptyBorder = new EmptyBorder(1)
|
private static def linedBorder = BorderFactory.createLineBorder(Color.BLACK)
|
||||||
|
private static def emptyBorder = BorderFactory.createEmptyBorder()
|
||||||
|
|
||||||
private def colors = [Color.getHSBColor(0.32f, 1f, 1f),
|
private def colors = [Color.getHSBColor(0.32f, 1f, 1f),
|
||||||
Color.getHSBColor(0.24f,1f,1f), Color.getHSBColor(0.16f, 1f, 1f),
|
Color.getHSBColor(0.24f,1f,1f), Color.getHSBColor(0.16f, 1f, 1f),
|
||||||
Color.getHSBColor(0.08f, 1f, 1f), Color.getHSBColor(0.0f, 1f, 1f)]
|
Color.getHSBColor(0.08f, 1f, 1f), Color.getHSBColor(0.0f, 1f, 1f)]
|
||||||
|
|
||||||
public PlayerListCellRenderer(Map params) {
|
public PlayerListCellRenderer(Map params, def tm) {
|
||||||
|
teamMaker = tm
|
||||||
showStats = params.showStats ?: false
|
showStats = params.showStats ?: false
|
||||||
colored = params.colored ?: false
|
colored = params.colored ?: false
|
||||||
chooser = params.chooser ?: WeightedChooser.getInstance()
|
|
||||||
|
|
||||||
setLayout(new BorderLayout())
|
setLayout(new BorderLayout())
|
||||||
setOpaque(colored)
|
setOpaque(colored)
|
||||||
@ -39,7 +41,7 @@ public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
|
|||||||
|
|
||||||
if (showStats) {
|
if (showStats) {
|
||||||
statsLabel = new JLabel()
|
statsLabel = new JLabel()
|
||||||
statsLabel.horizontalAlignment = JLabel.TRAILING,
|
statsLabel.horizontalAlignment = JLabel.TRAILING
|
||||||
statsLabel.opaque = false
|
statsLabel.opaque = false
|
||||||
add(statsLabel, BorderLayout.EAST)
|
add(statsLabel, BorderLayout.EAST)
|
||||||
}
|
}
|
||||||
@ -55,8 +57,11 @@ public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
|
|||||||
if (isSelected) setBorder(linedBorder)
|
if (isSelected) setBorder(linedBorder)
|
||||||
else setBorder(emptyBorder)
|
else setBorder(emptyBorder)
|
||||||
|
|
||||||
if (showStats)
|
if (showStats) {
|
||||||
statsLabel.text = value.gamesSat
|
int odds = Math.round(teamMaker.playerChooser.calculateOdds(
|
||||||
|
list.model, teamMaker.spotsOpen, value) * 100)
|
||||||
|
statsLabel.text = "${value.gamesSat} (${odds}%)"
|
||||||
|
}
|
||||||
|
|
||||||
if (colored) {
|
if (colored) {
|
||||||
def c = colors[Math.min(4, value.gamesSat)]
|
def c = colors[Math.min(4, value.gamesSat)]
|
||||||
@ -65,4 +70,5 @@ public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
|
|||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
package com.jdbernard.teammaker
|
|
||||||
|
|
||||||
public class RandomChooser extends PlayerChooser {
|
|
||||||
|
|
||||||
public Player choose(def players) {
|
|
||||||
def choices = []
|
|
||||||
players.each { choices << it }
|
|
||||||
chooseRandomly(choices)
|
|
||||||
}
|
|
||||||
|
|
||||||
public float calculateOdds(def players, def player) {
|
|
||||||
return 1f / (float) players
|
|
||||||
}
|
|
||||||
}
|
|
14
src/com/jdbernard/teammaker/RandomPlayerChooser.groovy
Normal file
14
src/com/jdbernard/teammaker/RandomPlayerChooser.groovy
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
package com.jdbernard.teammaker
|
||||||
|
|
||||||
|
public class RandomPlayerChooser extends AbstractPlayerChooser {
|
||||||
|
|
||||||
|
public Player choose(def players) {
|
||||||
|
def choices = []
|
||||||
|
players.each { choices << it }
|
||||||
|
return chooseRandomly(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getOdds(def players, Player player) {
|
||||||
|
return 1f / (float) players.size()
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ public class TeamMaker {
|
|||||||
/* ======== MODEL ======== */
|
/* ======== MODEL ======== */
|
||||||
|
|
||||||
public static def swing = new SwingBuilder()
|
public static def swing = new SwingBuilder()
|
||||||
public static final String version = "0.2"
|
public static final String version = "0.3"
|
||||||
|
|
||||||
def frame
|
def frame
|
||||||
def team1List
|
def team1List
|
||||||
@ -24,10 +24,13 @@ public class TeamMaker {
|
|||||||
def newGameButton
|
def newGameButton
|
||||||
def sittingList
|
def sittingList
|
||||||
|
|
||||||
PlayerChooser chooser = new RandomChooser()
|
AbstractPlayerChooser playerChooser = GameWeightedChooser.getInstance()
|
||||||
|
|
||||||
@Bindable boolean inGame = false
|
class Observables {
|
||||||
@Bindable int teamSize = 4
|
@Bindable boolean inGame = false
|
||||||
|
@Bindable int teamSize = 4
|
||||||
|
}
|
||||||
|
def model = new Observables()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -77,8 +80,8 @@ public class TeamMaker {
|
|||||||
label('Team B', constraints: gbc(gridx: 1, gridy: 0,
|
label('Team B', constraints: gbc(gridx: 1, gridy: 0,
|
||||||
fill: GBC.BOTH, insets: [5, 5, 0, 5]))
|
fill: GBC.BOTH, insets: [5, 5, 0, 5]))
|
||||||
|
|
||||||
def teamListRenderer = new PlayerListCellRenderer(showStats: false,
|
def teamListRenderer = new PlayerListCellRenderer(this,
|
||||||
colored: false)
|
showStats: false, colored: false)
|
||||||
|
|
||||||
team1List = list(cellRenderer: teamListRenderer,
|
team1List = list(cellRenderer: teamListRenderer,
|
||||||
constraints: gbc(gridx: 0, gridy: 1, fill: GBC.BOTH,
|
constraints: gbc(gridx: 0, gridy: 1, fill: GBC.BOTH,
|
||||||
@ -88,20 +91,8 @@ public class TeamMaker {
|
|||||||
team1WinsButton = button('Team A Wins',
|
team1WinsButton = button('Team A Wins',
|
||||||
constraints: gbc(gridx: 0, gridy: 2, anchor:
|
constraints: gbc(gridx: 0, gridy: 2, anchor:
|
||||||
GBC.CENTER, insets: [5, 5, 5, 0]),
|
GBC.CENTER, insets: [5, 5, 5, 0]),
|
||||||
enabled: false,
|
enabled: bind { model.inGame },
|
||||||
actionPerformed: {
|
actionPerformed: { declareWinner(team1List)})
|
||||||
sittingList.model.each { it.gamesSat++ }
|
|
||||||
team1List.model.each { player ->
|
|
||||||
player.gamesSat = 0
|
|
||||||
sittingList.model.addElement(player)
|
|
||||||
}
|
|
||||||
team1List.model.clear()
|
|
||||||
team1List.repaint()
|
|
||||||
sittingList.repaint()
|
|
||||||
inGame = false
|
|
||||||
team1WinsButton.enabled = false
|
|
||||||
team2WinsButton.enabled = false
|
|
||||||
})
|
|
||||||
|
|
||||||
team2List = list(cellRenderer: teamListRenderer,
|
team2List = list(cellRenderer: teamListRenderer,
|
||||||
constraints: gbc(gridx: 1, gridy: 1, fill: GBC.BOTH,
|
constraints: gbc(gridx: 1, gridy: 1, fill: GBC.BOTH,
|
||||||
@ -111,20 +102,8 @@ public class TeamMaker {
|
|||||||
team2WinsButton = button('Team B Wins',
|
team2WinsButton = button('Team B Wins',
|
||||||
constraints: gbc(gridx: 1, gridy: 2,
|
constraints: gbc(gridx: 1, gridy: 2,
|
||||||
anchor: GBC.CENTER, insets: [5, 5, 5, 5]),
|
anchor: GBC.CENTER, insets: [5, 5, 5, 5]),
|
||||||
enabled: false,
|
enabled: bind { model.inGame },
|
||||||
actionPerformed: {
|
actionPerformed: { declareWinner(team2List)} )
|
||||||
sittingList.model.each { it.gamesSat++ }
|
|
||||||
team2List.model.each { player ->
|
|
||||||
player.gamesSat = 0
|
|
||||||
sittingList.model.addElement(player)
|
|
||||||
}
|
|
||||||
team2List.model.clear()
|
|
||||||
team2List.repaint()
|
|
||||||
sittingList.repaint()
|
|
||||||
inGame = false
|
|
||||||
team1WinsButton.enabled = false
|
|
||||||
team2WinsButton.enabled = false
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,25 +112,18 @@ public class TeamMaker {
|
|||||||
border: titledBorder(title: 'Sitting Players')) {
|
border: titledBorder(title: 'Sitting Players')) {
|
||||||
|
|
||||||
sittingList = list(cellRenderer: new PlayerListCellRenderer(
|
sittingList = list(cellRenderer: new PlayerListCellRenderer(
|
||||||
showStats: true, colored: true),
|
this, showStats: true, colored: true),
|
||||||
model: new SortedPlayerModel())
|
model: new SortedPlayerModel())
|
||||||
}
|
}
|
||||||
|
|
||||||
button('Next Game', constraints: gbc(gridx: 0, gridy: 1,
|
button('Next Game', constraints: gbc(gridx: 0, gridy: 1,
|
||||||
anchor: GBC.CENTER, insets: [5, 5, 5, 0]),
|
anchor: GBC.CENTER, insets: [5, 5, 5, 0]),
|
||||||
|
enabled: bind { !model.inGame},
|
||||||
actionPerformed: { newGame() })
|
actionPerformed: { newGame() })
|
||||||
|
|
||||||
button('Add Player', constraints: gbc(gridx: 1, gridy: 1,
|
button('Add Player', constraints: gbc(gridx: 1, gridy: 1,
|
||||||
anchor: GBC.CENTER, insets: [5, 5, 5, 0]),
|
anchor: GBC.CENTER, insets: [5, 5, 5, 0]),
|
||||||
actionPerformed: {
|
actionPerformed: { addPlayer() })
|
||||||
def name = JOptionPane.showInputDialog(frame,
|
|
||||||
"Enter the new player's name: ", "New Player...",
|
|
||||||
JOptionPane.QUESTION_MESSAGE)
|
|
||||||
def player = new Player()
|
|
||||||
player.name = name
|
|
||||||
player.gamesSat = 0
|
|
||||||
sittingList.model.addElement(player)
|
|
||||||
})
|
|
||||||
|
|
||||||
newGameButton = button('Delete Player',
|
newGameButton = button('Delete Player',
|
||||||
constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.CENTER,
|
constraints: gbc(gridx: 2, gridy: 1, anchor: GBC.CENTER,
|
||||||
@ -178,41 +150,58 @@ public class TeamMaker {
|
|||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
||||||
private void newGame() {
|
public int getSpotsOpen() {
|
||||||
if (team1List.model.size() < teamSize)
|
def spots = (model.teamSize - team1List.model.size()) +
|
||||||
populate(team1List, sittingList, teamSize)
|
(model.teamSize - team2List.model.size())
|
||||||
|
return (spots == 0 ? model.teamSize : spots)
|
||||||
if (team2List.model.size() < teamSize)
|
|
||||||
populate(team2List, sittingList, teamSize)
|
|
||||||
|
|
||||||
team1WinsButton.enabled = true
|
|
||||||
team2WinsButton.enabled = true
|
|
||||||
newGameButton.enabled = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void populate(def teamList, def sittingList, int teamSize) {
|
private void addPlayer() {
|
||||||
while (teamList.model.size() < teamSize) {
|
def name = JOptionPane.showInputDialog(frame,
|
||||||
def player = choosePlayer(sittingList)
|
"Enter the new player's name: ", "New Player...",
|
||||||
|
JOptionPane.QUESTION_MESSAGE)
|
||||||
|
|
||||||
|
if (!name) return
|
||||||
|
|
||||||
|
def player = new Player()
|
||||||
|
player.name = name
|
||||||
|
player.gamesSat = 0
|
||||||
|
sittingList.model.addElement(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
private void newGame() {
|
||||||
|
if (team1List.model.size() < model.teamSize)
|
||||||
|
populate(team1List)
|
||||||
|
|
||||||
|
if (team2List.model.size() < model.teamSize)
|
||||||
|
populate(team2List)
|
||||||
|
|
||||||
|
model.inGame = true
|
||||||
|
//team1WinsButton.enabled = true
|
||||||
|
//team2WinsButton.enabled = true
|
||||||
|
//newGameButton.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private void declareWinner(def teamList) {
|
||||||
|
sittingList.model.each { it.gamesSat++ }
|
||||||
|
teamList.model.each { player ->
|
||||||
|
player.gamesSat = 0
|
||||||
|
sittingList.model.addElement(player)
|
||||||
|
}
|
||||||
|
teamList.model.clear()
|
||||||
|
teamList.repaint()
|
||||||
|
sittingList.repaint()
|
||||||
|
model.inGame = false
|
||||||
|
//team1WinsButton.enabled = false
|
||||||
|
//team2WinsButton.enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private void populate(def teamList) {
|
||||||
|
while (teamList.model.size() < model.teamSize) {
|
||||||
|
def player = playerChooser.choose(sittingList.model)
|
||||||
teamList.model.addElement(player)
|
teamList.model.addElement(player)
|
||||||
sittingList.model.removeElement(player)
|
sittingList.model.removeElement(player)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Player choosePlayer(def list) {
|
|
||||||
def choices = []
|
|
||||||
list.model.each { player ->
|
|
||||||
player.gamesSat.times { choices << player }
|
|
||||||
}
|
|
||||||
|
|
||||||
// if no players have sat at least one game, add all once
|
|
||||||
if (choices.size() == 0) list.model.each { player -> choices << player }
|
|
||||||
|
|
||||||
choices.sort { rand.nextInt() }
|
|
||||||
println choices
|
|
||||||
return choices[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
private float calculatePercentage(Player p, def sittingList) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
28
src/com/jdbernard/teammaker/WeightedChooser.groovy
Normal file
28
src/com/jdbernard/teammaker/WeightedChooser.groovy
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package com.jdbernard.teammaker
|
||||||
|
|
||||||
|
public abstract class WeightedChooser extends AbstractPlayerChooser {
|
||||||
|
|
||||||
|
int hardLimit = 5
|
||||||
|
|
||||||
|
public Player choose(def players) {
|
||||||
|
def choices = []
|
||||||
|
|
||||||
|
// find sitting threshold (longest sat - hard limit)
|
||||||
|
int threshold = (player.max { it.gamesSat }) - hardLimit
|
||||||
|
|
||||||
|
// add players, ignoring those past the hard limit
|
||||||
|
players.each { player ->
|
||||||
|
if (player.gamesSat >= threshold) {
|
||||||
|
choices << player
|
||||||
|
player.gamesSat.times { choices << player }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chooseRandomly(choices)
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getOdds(def players, Player player) {
|
||||||
|
def playerGames = player.gamesSat + 1
|
||||||
|
def totalGames = players.sum { it.gamesSat + 1 }
|
||||||
|
return (float) playerGames / (float) totalGames
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user