Reorganized odds structure.

This commit is contained in:
Jonathan Bernard 2010-07-06 08:07:09 -05:00
parent 242fbf2e56
commit 5d6f5882de
12 changed files with 126 additions and 20 deletions

View File

@ -9,6 +9,12 @@
</fileset> </fileset>
</path> </path>
<path id="lib.class.path">
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</path>
<taskdef name="groovyc" <taskdef name="groovyc"
classpathref="groovy.class.path" classpathref="groovy.class.path"
classname="org.codehaus.groovy.ant.Groovyc"/> classname="org.codehaus.groovy.ant.Groovyc"/>
@ -24,8 +30,12 @@
<target name="compile" depends="init"> <target name="compile" depends="init">
<groovyc <groovyc
srcdir="${src.dir}" srcdir="${src.dir}"
destdir="${build.dir}/classes" destdir="${build.dir}/classes">
classpathref="groovy.class.path"/> <classpath>
<path refid="groovy.class.path"/>
<path refid="lib.class.path"/>
</classpath>
</groovyc>
</target> </target>
<target name="increment-build-number"> <target name="increment-build-number">
@ -36,12 +46,19 @@
</target> </target>
<target name="build" depends="compile,increment-build-number"> <target name="build" depends="compile,increment-build-number">
<copy todir="${build.dir}/classes" file="log4j.properties"/>
<unjar dest="${build.dir}/classes"> <unjar dest="${build.dir}/classes">
<fileset dir="${env.GROOVY_HOME}/embeddable"> <fileset dir="${env.GROOVY_HOME}/embeddable">
<include name="*.jar"/> <include name="*.jar"/>
</fileset> </fileset>
</unjar> </unjar>
<unjar dest="${build.dir}/classes">
<fileset dir="${lib.dir}">
<include name="*.jar"/>
</fileset>
</unjar>
<jar <jar
destfile="${build.dir}/${app.name}-${app.version}.${build.number}.jar" destfile="${build.dir}/${app.name}-${app.version}.${build.number}.jar"
basedir="${build.dir}/classes"> basedir="${build.dir}/classes">

Binary file not shown.

BIN
lib/log4j-1.2.15.jar Normal file

Binary file not shown.

Binary file not shown.

10
log4j.properties Normal file
View File

@ -0,0 +1,10 @@
log4j.rootLogger=TRACE,stdout
log4j.com.jdbernard.teammaker=TRACE,file
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.SimpleLayout
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.file=teammaker.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.pattern=%-5p (%c)[%t]: %m%n

View File

@ -1,6 +1,8 @@
#Tue, 06 Jul 2010 06:30:26 -0500
#Mon Jul 05 23:10:39 CDT 2010 #Mon Jul 05 23:10:39 CDT 2010
app.version=0.3.0 app.version=0.3.0
build.number=47 build.number=67
src.dir=src src.dir=src
lib.dir=lib
build.dir=build build.dir=build
app.name=team-maker app.name=team-maker

View File

@ -1,14 +1,26 @@
package com.jdbernard.teammaker package com.jdbernard.teammaker
import org.apache.log4j.Logger
public class OddsCalculator { public class OddsCalculator {
Map odds = [:] Map odds = [:]
protected Logger log = Logger.getLogger(getClass())
public void recalculate(List players, int spotsOpen, PlayerChooser chooser) { public void recalculate(List players, int spotsOpen, PlayerChooser chooser) {
if (log.isTraceEnabled()) log.trace("Recalculating odds.")
// reset computed player odds // reset computed player odds
odds = [:] odds = [:]
// first check the trivial case: more spots than players
if (spotsOpen >= players.size()) {
players.each { odds[(it)] = 1f }
return
}
/* this list will contain each possible selection path with /* this list will contain each possible selection path with
* the odds at each node. It is a list of lists of lists:: * the odds at each node. It is a list of lists of lists::
* *
@ -24,7 +36,7 @@ public class OddsCalculator {
* ] * ]
*/ */
def oddsList = buildOdds([], players, spotsOpen) def oddsList = buildOdds([], players, spotsOpen, chooser)
/* The oddsMap is a condensed version of the oddsList, calculating /* The oddsMap is a condensed version of the oddsList, calculating
* the overall probability of each path:: * the overall probability of each path::
@ -67,9 +79,12 @@ public class OddsCalculator {
odds[(player)] = playerOdds odds[(player)] = playerOdds
} }
if (log.isTraceEnabled()) log.trace("Finished recalculating odds.")
} }
protected List buildOdds(List chosenPath, List players, int spotsOpen) { protected List buildOdds(List chosenPath, List players, int spotsOpen,
PlayerChooser chooser) {
def oddsList = [] def oddsList = []
if (spotsOpen == 0) return [chosenPath] if (spotsOpen == 0) return [chosenPath]
@ -78,7 +93,8 @@ public class OddsCalculator {
def odds = chooser.getOdds(players, player) def odds = chooser.getOdds(players, player)
def newPath = chosenPath.clone() def newPath = chosenPath.clone()
newPath << [player, odds] newPath << [player, odds]
oddsList.addAll(buildOdds(newPath, players - [player], spotsOpen - 1)) oddsList.addAll(buildOdds(newPath, players - [player],
spotsOpen - 1, chooser))
} }
return oddsList return oddsList

View File

@ -1,13 +1,29 @@
package com.jdbernard.teammaker package com.jdbernard.teammaker
import org.apache.log4j.Logger
public abstract class PlayerChooser { public abstract class PlayerChooser {
// ======== CLASS DATA ======== //
protected static final Random rand = new Random(System.currentTimeMillis()) protected static final Random rand = new Random(System.currentTimeMillis())
// ======== PUBLIC METHODS ======== //
public abstract Player choose (def players) public abstract Player choose (def players)
public abstract float getOdds(def players, Player player) public abstract float getOdds(def players, Player player)
// ======== INSTANCE DATA ======== //
protected Logger log = Logger.getLogger(getClass())
// ======== NON-PUBLIC METHODS ======== //
protected Player chooseRandomly(List choices) { protected Player chooseRandomly(List choices) {
if (log.isTraceEnabled())
log.trace("Choosing randomly from: ${choices})")
choices.sort { rand.nextInt() } choices.sort { rand.nextInt() }
if (log.isTraceEnabled()) log.trace("Chose: ${choices[0]}")
return choices[0]
} }
} }

View File

@ -58,8 +58,8 @@ public class PlayerListCellRenderer extends JPanel implements ListCellRenderer {
else setBorder(emptyBorder) else setBorder(emptyBorder)
if (showStats) { if (showStats) {
int odds = Math.round(teamMaker.playerChooser.calculateOdds( def odds = teamMaker.oddsCalculator.odds[(value)]
list.model, teamMaker.spotsOpen, value) * 100) odds = odds ? Math.round(odds * 100) : "?"
statsLabel.text = "${value.gamesSat} (${odds}%)" statsLabel.text = "${value.gamesSat} (${odds}%)"
} }

View File

@ -1,6 +1,6 @@
package com.jdbernard.teammaker package com.jdbernard.teammaker
public class RandomPlayerChooser extends AbstractPlayerChooser { public class RandomPlayerChooser extends PlayerChooser {
public Player choose(def players) { public Player choose(def players) {
def choices = [] def choices = []

View File

@ -8,6 +8,7 @@ import java.awt.BorderLayout
import javax.swing.DefaultListModel import javax.swing.DefaultListModel
import javax.swing.JFrame import javax.swing.JFrame
import javax.swing.JOptionPane import javax.swing.JOptionPane
import org.apache.log4j.Logger
public class TeamMaker { public class TeamMaker {
@ -24,7 +25,10 @@ public class TeamMaker {
def newGameButton def newGameButton
def sittingList def sittingList
AbstractPlayerChooser playerChooser = GameWeightedChooser.getInstance() protected Logger log = Logger.getLogger(getClass())
PlayerChooser playerChooser = new WeightedChooser(hardLimit: 2)
OddsCalculator oddsCalculator = new OddsCalculator()
class Observables { class Observables {
@Bindable boolean inGame = false @Bindable boolean inGame = false
@ -134,6 +138,7 @@ public class TeamMaker {
} }
} }
} }
@ -147,6 +152,7 @@ public class TeamMaker {
} }
TeamMaker() { TeamMaker() {
log.fatal("Is this thing on?")
init() init()
} }
@ -167,6 +173,13 @@ public class TeamMaker {
player.name = name player.name = name
player.gamesSat = 0 player.gamesSat = 0
sittingList.model.addElement(player) sittingList.model.addElement(player)
// recalculate odds
def players = sittingList.model.collect { it }
swing.doOutside {
oddsCalculator.recalculate(players, getSpotsOpen(), playerChooser)
swing.edt { sittingList.repaint() }
}
} }
private void newGame() { private void newGame() {
@ -177,9 +190,13 @@ public class TeamMaker {
populate(team2List) populate(team2List)
model.inGame = true model.inGame = true
//team1WinsButton.enabled = true
//team2WinsButton.enabled = true // recalculate odds
//newGameButton.enabled = false def players = sittingList.model.collect { it }
swing.doOutside {
oddsCalculator.recalculate(players, getSpotsOpen(), playerChooser)
swing.edt { sittingList.repaint() }
}
} }
private void declareWinner(def teamList) { private void declareWinner(def teamList) {
@ -188,12 +205,18 @@ public class TeamMaker {
player.gamesSat = 0 player.gamesSat = 0
sittingList.model.addElement(player) sittingList.model.addElement(player)
} }
//recalculate odds
def players = sittingList.model.collect { it }
swing.doOutside {
oddsCalculator.recalculate(players, getSpotsOpen(), playerChooser)
swing.edt { sittingList.repaint() }
}
teamList.model.clear() teamList.model.clear()
teamList.repaint() teamList.repaint()
sittingList.repaint() sittingList.repaint()
model.inGame = false model.inGame = false
//team1WinsButton.enabled = false
//team2WinsButton.enabled = false
} }
private void populate(def teamList) { private void populate(def teamList) {

View File

@ -1,14 +1,20 @@
package com.jdbernard.teammaker package com.jdbernard.teammaker
public abstract class WeightedChooser extends AbstractPlayerChooser { public class WeightedChooser extends PlayerChooser {
int hardLimit = 5 int hardLimit = 5
public Player choose(def players) { public Player choose(def players) {
// make a proper list
players = players.collect { it }
def choices = [] def choices = []
def threshold = getThreshold(players)
// find sitting threshold (longest sat - hard limit) if (log.isTraceEnabled()) {
int threshold = (player.max { it.gamesSat }) - hardLimit log.trace("Choosing a player weighted by games sat.")
log.trace("Players: $players")
log.trace("Threshold: $threshold")
}
// add players, ignoring those past the hard limit // add players, ignoring those past the hard limit
players.each { player -> players.each { player ->
@ -17,12 +23,28 @@ public abstract class WeightedChooser extends AbstractPlayerChooser {
player.gamesSat.times { choices << player } player.gamesSat.times { choices << player }
} }
} }
return chooseRandomly(choices) return chooseRandomly(choices)
} }
public float getOdds(def players, Player player) { public float getOdds(def players, Player player) {
def playerGames = player.gamesSat + 1 def threshold = getThreshold(players)
def totalGames = players.sum { it.gamesSat + 1 }
def playerGames = player.gamesSat >= threshold ?
player.gamesSat + 1 : 0
def totalGames = players.sum {
it.gamesSat >= threshold ? it.gamesSat + 1 : 0
}
if (log.isTraceEnabled())
log.trace("Odds for $player: $playerGames / $totalGames = " +
((float) playerGames / (float) totalGames))
return (float) playerGames / (float) totalGames return (float) playerGames / (float) totalGames
} }
protected int getThreshold(def players) {
return (players.max { it.gamesSat }).gamesSat - hardLimit
}
} }