Page 1 of 11

Posted: Fri Sep 01, 2006 3:23 am
by Tigereye
The following is the Sql procedure that is run whenever a game posts to ASGS:

Code: Select all

-- WHEN GAME DOESN'T COUNT NO ELO CHANGES ARE RECORDED.
DECLARE @GameCounted int
    SET @GameCounted = 1

-- TO BE OUTPUT VIA THE WEBSERVICE
DECLARE @GameReason varchar(100)
    SET @GameReason = 'Game Counted'

-- GET GAME LENGTH
DECLARE @GameDuration real
    SET @GameDuration = (SELECT DATEDIFF(second,GameStartTime,GameEndTime) FROM Game WHERE GameIdentID = @GameID)

-- SHOULDN'T HAPPEN
IF @GameDuration < 1 
BEGIN
  RAISERROR('Game Duration was Zero or Negative',16,1)
  RETURN
END

-- WHOLE GAME LESS THAN 5 MINUTES (300 SECONDS)
IF @GameDuration < 300
BEGIN
  SET @GameCounted = 0
  SET @GameReason = 'Game duration was less than 5 minutes'
END

-- GET ACCOUNT LISTS FOR REFERENCING
        DECLARE @Accounts TABLE (Member_ID int, Callsign varchar(50), TeamNumber int, GameTime real, AdjustedGameTime real, Winner int)
    INSERT INTO @Accounts (Member_ID, Callsign, TeamNumber, GameTime, AdjustedGameTime, Winner)
SELECT DISTINCT gtm.GameTeamMemberMemberID,gtm.GameTeamMemberCallsign, gt.GameTeamNumber, gtm.GameTeamMemberDuration, gtm.GameTeamMemberDuration, gt.GameTeamWinner
           FROM GameTeamMember gtm
     INNER JOIN GameTeam gt ON gt.GameTeamIdentID = gtm.GameTeamID
          WHERE gt.GameID = @GameID

-- IF THEY PLAYED LESS THAN 5 MINUTES, THEY DIDN'T PLAY AT ALL
         UPDATE @Accounts
            SET GameTime = 0
          WHERE GameTime < 300

-- WORKAROUND FOR EARLY DURATION BUG
         UPDATE @Accounts
            SET GameTime = @GameDuration
          WHERE GameTime > @GameDuration

-- LOSERS WHO DROP GET DEDUCTED FULL POINTS
         UPDATE @Accounts
            SET AdjustedGameTime = dbo.ASGSGetPlayerMaxGameTime(@GameID, Member_ID)
          WHERE Winner = 0
            AND AdjustedGameTime >= 300

-- LOSERS WHO SWITCH TEAMS ONLY GET CREDIT FOR LOSING
         DELETE FROM @Accounts
          WHERE Winner = 1
            AND Member_ID IN (SELECT Member_ID FROM @Accounts WHERE Winner = 0)

-- DON'T COUNT GAMES WITH LESS THAN 10 FOR AT LEAST HALF THE GAME
IF (SELECT COUNT(*) FROM @Accounts WHERE ABS(@GameDuration - GameTime) < (@GameDuration / 2)) < 10
BEGIN
  SET @GameCounted = 0
  SET @GameReason = 'Less than 10 players for at least half of the game'
END

-- INIT PLAYERLIST
    DECLARE @PlayerList TABLE (Member_ID int, Playtime int, Modifier decimal(4,2), AdjustedModifier decimal(4,2), TeamNumber int)
INSERT INTO @PlayerList (Member_ID, Playtime, Modifier, AdjustedModifier,TeamNumber)
 SELECT MAX(Member_ID), SUM(GameTime), SUM(GameTime/@GameDuration), SUM(AdjustedGameTime/@GameDuration), TeamNumber
       FROM @Accounts
   GROUP BY Member_ID, TeamNumber

-- DO THE TWO STEP BECAUSE SQL DOESN'T LIKE THE AGGREGATES
    DECLARE @Players TABLE (Member_ID int, ELORating int, Wins int, Losses int, Adjustment int, Playtime int, Modifier decimal(4,2), AdjustedModifier decimal(4,2), Expected real, TeamNumber int)
INSERT INTO @Players (Member_ID, ELORating, Wins, Losses, Adjustment, Playtime, Modifier, AdjustedModifier, Expected, TeamNumber)
 SELECT MAX(Member_ID), 1500, 0, 0, 0, MAX(Playtime), MAX(Modifier), MAX(AdjustedModifier), .50, TeamNumber
       FROM @PlayerList
   GROUP BY Member_ID, TeamNumber

-- FILL IN TRUE ELO VALUES FOR PLAYERS THAT HAVE EXISTING ELO RECORDS
    UPDATE @Players
       SET ELORating = e.ELORanking
      FROM ELO e
INNER JOIN @Players p ON p.Member_ID = e.Member_ID

-- CALCULATION VARIABLES
DECLARE @TeamID int
DECLARE @PlayerELO real
DECLARE @OpponentELO real
DECLARE @PlayerES real
DECLARE @OpponentES real
DECLARE @SCORE real
DECLARE @Adjustment int
DECLARE @K int

-- INIT WORKING LISTS
DECLARE @TeamELO TABLE (TeamNumber int, TeamPlayerCount int, ELORating int, Winner real, OpponentELO int, Expected real, Adjustment int, Playtime int, MaxPlayertime int)
DECLARE @Teams TABLE (TeamID int)

-- BEGIN PROCESSING GAME
INSERT INTO @TeamELO(TeamNumber, TeamPlayerCount, ELORating, Winner, Playtime, MaxPlayertime)
     SELECT gt.GameTeamNumber, Count(p.Member_ID), SUM(((p.ELORating - 1500) * p.Modifier) + 1500) / Count(*), gt.GameTeamWinner, SUM(p.Playtime), MAX(p.Playtime)
       FROM GameTeamMember gtm, GameTeam gt, @Players p
      WHERE gt.GameTeamIdentID = gtm.GameTeamID
        AND p.Member_ID = gtm.GameTeamMemberMemberID
        AND p.TeamNumber = gt.GameTeamNumber
        AND gt.GameID = @GameID
        AND p.Playtime > 0
   GROUP BY gt.GameTeamNumber, gt.GameTeamWinner, p.TeamNumber

-- ADJUST TEAM ELO TO ACCOUNT FOR IMBALANCE
DECLARE @MaxPlaytime int
    SET @MaxPlaytime = (SELECT MAX(Playtime) FROM @TeamELO)

DECLARE @MaxPlayertime int
    SET @MaxPlayertime = (SELECT MAX(MaxPlayertime) FROM @TeamELO)

 UPDATE @TeamELO
    SET ELORating = ELORating * (CONVERT(real,Playtime) / CONVERT(real,@MaxPlaytime))

-- PROCESS GAME
INSERT INTO @Teams(TeamID)
     SELECT TeamNumber FROM @TeamELO

-- INNER LOOP - RUNS ONCE FOR EACH TEAM
WHILE (SELECT COUNT(*) FROM @Teams) > 0
BEGIN

  SET @TeamID = (SELECT TOP 1 TeamID FROM @Teams)

-- ELO IS 1 VS 1 BASED, SO CREATE AN AGGREGATE OPPONENT SCORE
  UPDATE @TeamELO
     SET OpponentELO = (SELECT SUM(ELORating * Playtime) / SUM(Playtime) FROM @TeamELO WHERE TeamNumber <> @TeamID)
   WHERE TeamNumber = @TeamID

-- IF NOBODY WON, DECLARE A DRAW
  IF (SELECT MAX(Winner) FROM @TeamELO) = 0
  BEGIN
    UPDATE @TeamELO SET Winner = .5
  END

-- ELO FORMULA BASED ON STANDARD ELO
  SET @PlayerELO = (SELECT ELORating FROM @TeamELO WHERE TeamNumber = @TeamID)
  SET @OpponentELO = (SELECT OpponentELO FROM @TeamELO WHERE TeamNumber = @TeamID)

  SET @PlayerES = (1 / (1 + (POWER(CONVERT(real,10),((@OpponentELO - @PlayerELO) / 400)))))
  SET @OpponentES = (1 / (1 + (POWER(CONVERT(real,10),((@PlayerELO - @OpponentELO) / 400)))))

  SET @SCORE = (SELECT Winner FROM @TeamELO WHERE TeamNumber = @TeamID)
  SET @K = 32

  SET @Adjustment = @K * (@Score - @PlayerES)

-- UPDATE THE WORK TABLE
  UPDATE @TeamELO
     SET Adjustment = @Adjustment,
         Expected = @PlayerES
   WHERE TeamNumber = @TeamID

  DELETE FROM @Teams WHERE TeamID = @TeamID

END

-- USED DURING DEBUGGING ONLY
IF @OutputResults = 1
BEGIN
  SELECT * FROM @TeamELO
END

-- GAME PROCESSING COMPLETE

-- CHECK FOR ELO IMBALANCE (STACK)
IF (SELECT MAX(Expected) FROM @TeamELO WHERE Winner = 1) > .70
BEGIN
  SET @GameCounted = 0
  SET @GameReason = 'Game imbalance was excessive'
END

-- USED DURING DEBUGGING ONLY
IF @DebugMode = 1
BEGIN
  SELECT * FROM @TeamELO
  SELECT * FROM @Players
  SELECT * FROM @Accounts
  RETURN
END

-- IF GAME COUNTED, UPDATE ALL THE STUFF
IF @GameCounted = 1
BEGIN

      UPDATE @Players
         SET Wins = Wins + gt.GameTeamWinner,
             Losses = Losses + 1 - gt.GameTeamWinner,
             Adjustment = t.Adjustment,
             Expected = t.Expected
        FROM @TeamELO t
  INNER JOIN GameTeam gt ON gt.GameTeamNumber = t.TeamNumber
  INNER JOIN GameTeamMember gtm ON gtm.GameTeamID = gt.GameTeamIdentID
  INNER JOIN @Accounts a ON a.Callsign = gtm.GameTeamMemberCallsign
  INNER JOIN @Players p ON p.Member_ID = a.Member_ID
       WHERE gt.GameID = @GameID
  
  INSERT INTO GamePlayerELO (GameID, GamePlayerELOMemberID, GamePlayerELORating, GamePlayerELOAdjustment, GamePlayerELOModifier)
       SELECT @GameID, Member_ID, ELORating, Adjustment, Modifier
         FROM @Players

-- USED DURING DEBUGGING ONLY
  IF @OutputResults = 1
  BEGIN
    SELECT * FROM @Players
  END

-- CACHE KILLCOUNTS
DECLARE @AWE TABLE (Member_ID int, Kills int, Ejects int)
 INSERT INTO @AWE(Member_ID, Kills, Ejects)
 SELECT Member_ID, Kills, Ejects
   FROM dbo.ASGSNetGetGameAWECount(@GameID)

  --UPDATE LOOP
  DECLARE @Member_ID int

  WHILE (SELECT COUNT(*) FROM @Players) > 0
  BEGIN

    SET @Member_ID = (SELECT TOP 1 Member_ID FROM @Players)

    -- IF PLAYER ALREADY HAS AN ELO RECORD
    IF EXISTS(SELECT Member_ID FROM ELO WHERE Member_ID = @Member_ID)
    BEGIN
          UPDATE ELO
             SET Member_ID = p.Member_ID,
                 ELORanking = CASE 
                              WHEN ELORanking < 800 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 4)) 
                              WHEN ELORanking BETWEEN 800 AND 1500 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 2))
                              WHEN ELORanking BETWEEN 1500 AND 2200 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 1))
                              WHEN ELORanking > 2200 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * .5))
                              END,
                 ELOWins = ELOWins + p.Wins,
                 ELOLosses = ELOLosses + p.Losses,
                 ELOExpected = ((ELOExpected * (ELOWins + ELOLosses)) + p.Expected) / (ELOWins + ELOLosses + 1),
                 ELOKills = ELOKills + awe.Kills,
                 ELOEjects = ELOEjects + awe.Ejects
             FROM @Players p
      INNER JOIN ELO e ON e.Member_ID = p.Member_ID
      INNER JOIN @AWE awe ON awe.Member_ID = p.Member_ID
           WHERE p.Member_ID = @Member_ID
             AND p.Member_ID > 0

    END

    -- IF PLAYER HAS NO ELO RECORDS (NEW)
    ELSE
    BEGIN
          INSERT INTO ELO (Member_ID, ELORanking, ELOWins, ELOLosses, ELOExpected, ELOKills, ELOEjects)
               SELECT p.Member_ID, p.ELORating, p.Wins, p.Losses, p.Expected, awe.Kills, awe.Ejects
                 FROM @Players p
           INNER JOIN @AWE awe ON awe.Member_ID = p.Member_ID
                WHERE p.Member_ID = @Member_ID
                  AND p.Member_ID > 0
    END

    DELETE FROM @Players WHERE Member_ID = @Member_ID

  END

END

-- ARCHIVE GAME ELO HISTORY
INSERT INTO GameELO (GameID, GameELOTeamNumber, GameELORating, GameELOScore, GameELOOpponentRating, GameELOExpectedOutcome, GameELOAdjustment, GameELOCounted, GameELOReason)
     SELECT @GameID, TeamNumber, ELORating, Winner, OpponentELO, Expected, Adjustment, @GameCounted, @GameReason
       FROM @TeamELO

--CLEAN UP
  DELETE FROM @Teams
  DELETE FROM @TeamELO

Posted: Fri Sep 01, 2006 3:31 am
by jgbaxter
Thanks.

Questions...

Code: Select all

-- LOSERS WHO DROP GET DEDUCTED FULL POINTS
         UPDATE @Accounts
            SET AdjustedGameTime = dbo.ASGSGetPlayerMaxGameTime(@GameID, Member_ID)
          WHERE Winner = 0
            AND AdjustedGameTime >= 300
So if you play only on the losing team, you lose full points (unless you played 300 seconds or less)?

Code: Select all

-- LOSERS WHO SWITCH TEAMS ONLY GET CREDIT FOR LOSING
         DELETE FROM @Accounts
          WHERE Winner = 1
            AND Member_ID IN (SELECT Member_ID FROM @Accounts WHERE Winner = 0)
So regardless how many teams you play on, the only points that change are that you automatically lose full points for a lost game?


See, if my comments above are correct I'm gonna have to give Terra a charlie horse.

Posted: Fri Sep 01, 2006 5:49 am
by Terralthra
You were right, I was wrong. Mea culpa.

Posted: Fri Sep 01, 2006 6:08 am
by jgbaxter
Yea, sorry about my general bitchyness. /blush.gif" style="vertical-align:middle" emoid=":blush:" border="0" alt="blush.gif" />

Speaking of Latin, hey Terra, check out that bbc/hbo series Rome? Great series. /smile.gif" style="vertical-align:middle" emoid=":)" border="0" alt="smile.gif" />

Posted: Fri Sep 01, 2006 8:12 am
by Raveen
Good series, a prime example of the old saying "When in Rome, take off all your clothes and overact wildly". (Actually I rather liked it but don't let that get in the way of a good joke).

Is there any chance of getting this boiled down to plain english for the non-programmers among us?

Posted: Fri Sep 01, 2006 9:48 am
by Dengaroth
Raveen wrote:QUOTE (Raveen @ Sep 1 2006, 10:12 AM) Is there any chance of getting this boiled down to plain english for the non-programmers among us?
Just read the comments that start each block of code. It's a decent summary.

Only one comment: why is the "imbalance excessive" threshold still at .85? That's way too high... generally, anything above 70/30 is a lost cause.

Now you'll say that "for a 70/30 game, people will only get like one point"... but the game does count, which means kills will get credited. Which is motivation enough for some people.

Posted: Fri Sep 01, 2006 1:56 pm
by Cunnuk
When you take a broken system and break it even more, does it fix it?

The age (length), a player has played this game should have nothing to do with their ranks.

Why reward someone for playing a game for a length of time?
Reward them for the skill level they show in game, commanding, # of kills, base kills, assists, and even give them points for nannin bases and bomb runs.

Make the system mimic real life.
Are promotions give to someone just because they been around the longest?
In the real world, someone is promoted by what they do, not how long they've been there.

I do like the fact that if someone switches sides, they will not benefit from doing so.
And if you drop and the team loses, your points will reflect it.

Drop the age from the rankings system, then the system will reflect the player’s skill.

Posted: Fri Sep 01, 2006 2:26 pm
by tmc
2 beautiful reason why ELO cannot mathematically converge under its persent form:

QUOTE -- LOSERS WHO DROP GET DEDUCTED FULL POINTS
UPDATE @Accounts
SET AdjustedGameTime = dbo.ASGSGetPlayerMaxGameTime(@GameID, Member_ID)
WHERE Winner = 0
AND AdjustedGameTime >= 300

-- LOSERS WHO SWITCH TEAMS ONLY GET CREDIT FOR LOSING
DELETE FROM @Accounts
WHERE Winner = 1
AND Member_ID IN (SELECT Member_ID FROM @Accounts WHERE Winner = 0)[/quote]

Putting "penalties" into a rank whose sole purpose is balance makes absolutely no sense.

Posted: Fri Sep 01, 2006 2:48 pm
by jgbaxter
Actually Tmc, elo's not perfect, but the reason you quoted don't help your cause.

1- Is there to give players a chance to have a peek at a game and decide if the side they chose is worth playing.

2- Is there to mitigate those who would cheat by joining one game and leaving to join the winning team.



Posted: Fri Sep 01, 2006 3:15 pm
by jgbaxter

Code: Select all

-- IF PLAYER ALREADY HAS AN ELO RECORD
    IF EXISTS(SELECT Member_ID FROM ELO WHERE Member_ID = @Member_ID)
    BEGIN
          UPDATE ELO
             SET Member_ID = p.Member_ID,
                 ELORanking = CASE 
                              WHEN ELORanking < 800 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 4)) 
                              WHEN ELORanking BETWEEN 800 AND 1500 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 2))
                              WHEN ELORanking BETWEEN 1500 AND 2200 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * 1))
                              WHEN ELORanking > 2200 THEN ELORanking + (p.Adjustment * (p.AdjustedModifier * .5))
I'm a bit confused with the code above, from what I'm reading if you have elo ranking >2200 your agjustment turns it to 1500 at the extreme, while if your elo ranking is 2200, it stays at 2200?

Umm, isn't that contraindicative to what's trying to be done? /smile.gif" style="vertical-align:middle" emoid=":)" border="0" alt="smile.gif" />