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

View File

@ -1,14 +1,26 @@
package com.jdbernard.teammaker
import org.apache.log4j.Logger
public class OddsCalculator {
Map odds = [:]
protected Logger log = Logger.getLogger(getClass())
public void recalculate(List players, int spotsOpen, PlayerChooser chooser) {
if (log.isTraceEnabled()) log.trace("Recalculating odds.")
// reset computed player 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
* 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 overall probability of each path::
@ -67,9 +79,12 @@ public class OddsCalculator {
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 = []
if (spotsOpen == 0) return [chosenPath]
@ -78,7 +93,8 @@ public class OddsCalculator {
def odds = chooser.getOdds(players, player)
def newPath = chosenPath.clone()
newPath << [player, odds]
oddsList.addAll(buildOdds(newPath, players - [player], spotsOpen - 1))
oddsList.addAll(buildOdds(newPath, players - [player],
spotsOpen - 1, chooser))
}
return oddsList

View File

@ -1,13 +1,29 @@
package com.jdbernard.teammaker
import org.apache.log4j.Logger
public abstract class PlayerChooser {
// ======== CLASS DATA ======== //
protected static final Random rand = new Random(System.currentTimeMillis())
// ======== PUBLIC METHODS ======== //
public abstract Player choose (def players)
public abstract float getOdds(def players, Player player)
// ======== INSTANCE DATA ======== //
protected Logger log = Logger.getLogger(getClass())
// ======== NON-PUBLIC METHODS ======== //
protected Player chooseRandomly(List choices) {
if (log.isTraceEnabled())
log.trace("Choosing randomly from: ${choices})")
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)
if (showStats) {
int odds = Math.round(teamMaker.playerChooser.calculateOdds(
list.model, teamMaker.spotsOpen, value) * 100)
def odds = teamMaker.oddsCalculator.odds[(value)]
odds = odds ? Math.round(odds * 100) : "?"
statsLabel.text = "${value.gamesSat} (${odds}%)"
}

View File

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

View File

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

View File

@ -1,14 +1,20 @@
package com.jdbernard.teammaker
public abstract class WeightedChooser extends AbstractPlayerChooser {
public class WeightedChooser extends PlayerChooser {
int hardLimit = 5
public Player choose(def players) {
// make a proper list
players = players.collect { it }
def choices = []
def threshold = getThreshold(players)
// find sitting threshold (longest sat - hard limit)
int threshold = (player.max { it.gamesSat }) - hardLimit
if (log.isTraceEnabled()) {
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
players.each { player ->
@ -17,12 +23,28 @@ public abstract class WeightedChooser extends AbstractPlayerChooser {
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 }
def threshold = getThreshold(players)
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
}
protected int getThreshold(def players) {
return (players.max { it.gamesSat }).gamesSat - hardLimit
}
}