/*
* JOGRE (Java Online Gaming Real-time Engine) - API
* Copyright (C) 2004 Bob Marks (marksie531@yahoo.com)
* http://jogre.sourceforge.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package com.if3games.chessonline.utils;
import java.util.HashMap;
import java.util.StringTokenizer;
import com.if3games.chessonline.data.ConstantsData;
/**
* JOGRE's implementation of the ELO rating system. The following is an
* example of how to use the Elo Rating System.
* <code>
* EloRatingSystem elo = new EloRatingSystem();
* int userRating = 1600;
* int opponentRating = 1650;
* int newUserRating = elo.getNewRating(userRating, opponentRating, WIN);
* int newOpponentRating = elo.getNewRating(opponentRating, userRating, LOSS);
* </code>
*
* @author Garrett Lehman (gman)
*/
public class EloRatingSystem {
public final static int SUPPORTED_PLAYERS = 2;
// Score constants
public final static double WIN = 1.0;
public final static double DRAW = 0.5;
public final static double LOSS = 0.0;
// Attributes
private String game;
// List of singletons are stored in this HashMap
private static HashMap ratingSystems = null;
/**
* Constructor to the JOGRE ELO rating system.
*
* @param game Game to do the rating on as games may vary
* in their implementation of ELO.
*/
private EloRatingSystem (String game) {
this.game = game; // Set game.
}
/**
* Return instance of an ELO rating system.
*
* @param game Game to key of.
* @return ELO rating system for specified game.
*/
public static synchronized EloRatingSystem getInstance (String game) {
if (ratingSystems == null)
ratingSystems = new HashMap ();
// Retrieve rating system
Object ratingSystem = ratingSystems.get (game);
// If null then create new one and add to hash keying off the game
if (ratingSystem == null) {
ratingSystem = new EloRatingSystem (game);
ratingSystems.put (game, ratingSystem);
return (EloRatingSystem)ratingSystem;
}
else
return (EloRatingSystem)ratingSystem;
}
/**
* Convience overloaded version of getNewRating (int, int, double)
* which takes a result type and
*
* @param rating
* @param opponentRating
* @param resultType
* @param gameMode
* This param for K Factor: gameMode=1 - Blitz mode
* gameMode=0 - Standart game mode
* @param isNewbie
* for a player new to the rating list until he has completed events with a total of at least 30 games.
* true if <=30, false if > 30
* @return
*/
public int getNewRating (int rating, int opponentRating, int resultType, int gameMode, boolean isNewbie) {
switch (resultType) {
case ConstantsData.GAME_WON:
return getNewRating (rating, opponentRating, WIN, gameMode, isNewbie);
case ConstantsData.GAME_LOSS:
return getNewRating (rating, opponentRating, LOSS, gameMode, isNewbie);
case ConstantsData.GAME_DRAW:
return getNewRating (rating, opponentRating, DRAW, gameMode, isNewbie);
}
return -1; // no score this time.
}
/**
* Get new rating.
*
* @param rating
* Rating of either the current player or the average of the
* current team.
* @param opponentRating
* Rating of either the opponent player or the average of the
* opponent team or teams.
* @param score
* Score: 0=Loss 0.5=Draw 1.0=Win
* @return the new rating
*/
public int getNewRating(int rating, int opponentRating, double score, int gameMode, boolean isNewbie) {
double kFactor = getKFactor(rating, gameMode, isNewbie);
double expectedScore = getExpectedScore(rating, opponentRating);
int newRating = calculateNewRating(rating, score, expectedScore, kFactor);
return newRating;
}
/**
* Calculate the new rating based on the ELO standard formula.
* newRating = oldRating + constant * (score - expectedScore)
*
* @param oldRating Old Rating
* @param score Score
* @param expectedScore Expected Score
* @param constant Constant
* @return the new rating of the player
*/
private int calculateNewRating(int oldRating, double score, double expectedScore, double kFactor) {
return oldRating + (int) (kFactor * (score - expectedScore));
}
/**
* This is the standard chess constant. This constant can differ
* based on different games. The higher the constant the faster
* the rating will grow. That is why for this standard chess method,
* the constant is higher for weaker players and lower for stronger
* players.
*
* @param rating Rating
* @return Constant
*/
private double getKFactor (int rating, int gameMode, boolean isNewbie) {
if (isNewbie) {
return ConstantsData.ELO_K_FACTOR_NEWBIE;
} else if(gameMode == 1) {
return ConstantsData.ELO_K_FACTOR_BLITZ;
} else if(rating < 2400) {
return ConstantsData.ELO_K_FACTOR_DOWN2400;
} else if(rating >= 2400) {
return ConstantsData.ELO_K_FACTOR_UP2400;
}
return ConstantsData.DEFAULT_ELO_K_FACTOR;
}
/**
* Get expected score based on two players. If more than two players
* are competing, then opponentRating will be the average of all other
* opponent's ratings. If there is two teams against each other, rating
* and opponentRating will be the average of those players.
*
* @param rating Rating
* @param opponentRating Opponent(s) rating
* @return the expected score
*/
private double getExpectedScore (int rating, int opponentRating) {
return 1.0 / (1.0 + Math.pow(10.0, ((double) (opponentRating - rating) / 400.0)));
}
}