Create an account


Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Map, Server and Win Ratio statistics

#1
Hey all, inspired by Samual talking about different chances to win a match depending on your team color, I put some effort into data-mining the Xonstats DB to have a closer look at this issue Big Grin

I came up with a small Python script which filters & processes the data from all matches on Xonstats, and uses ROOT to generate two plots for each gametype: The first one shows the number of games played per server and per map (one axis each for server and map; the color shows the number of games). The second one shows the relative difference of the number of games won between red and blue. It is defined simply as ratio = (wins_red - wins_blue ) / total_games. Therefore, -1 means all games were won by red, and +1 means all were won by blue. This is also represented by the color in the according plot.

Note that I filtered out most of the Insta+Hook servers, mainly because the hook may even out map asymmetries and the like. Normal Insta and Overkill is still included.


[Image: HjeZLoJ.png] [Image: YvQwpYp.png] [Image: ls8B86L.png]

For example, one can see that on some (probably public) servers, red wins a majority of games. Smilecythe already suggested that the reason could be people joining the red team by default. On the pickup servers, that idea should not apply though.

One of my personal ideas to explain these red-blue asymmetry is – apart from obvious asymmetries in the maps (which we don't have except for maybe a small number of maps) – a chirality effect ("handed-ness"), meaning that it could make a difference if you have make a left or right turn when you attack.


Please tell me what you think of this, if you have any ideas etc. I would love to see some discussion on this IMHO very interesting topic =)

Link to all plots (ctf, tdm, ca,kh) in full-resolution: http://imgur.com/a/b047B/all


UPDATE: Added plot where win ratio is weighted by number of games (it turned out that most of the extreme values in the win ratio plot are caused by a really small number of games played).

UPDATE 2: Fixed kinda misleading colorscheme for the values. Using an even number of colors shifted the perceived mean value towards red, giving the wrong impression in the plots.


Python script to generate that data (input is simply a CSV file):

Code:
#!/usr/bin/env python

import ROOT
import sys
from collections import OrderedDict
from math import log
from array import array


TEAMS = { 5: 'red', 14: 'blue' }

#GAMETYPE = "ctf"
GAMETYPE = sys.argv[1]

MIN_GAMEID = 1

MAP_BLACKLIST = [
]

SERVER_BLACKLIST = [
    #"[MoN] Vehicle",
    #"[MoN] Overkill",
    #"Overkill",
    #"Overkill Mod",
    "{Minsta+Hook}",
    "[Minsta|Hook]",
    "[MinstaGib|Hook]",
    "MinstaGib+Hook",
    "Minsta+Hook",
    "Instagib+Hook",
]


NMAPS  = 50
NSERVERS = 50

NCOLORS = 99

CANVAS_SIZE = 2000

CRED   = "\033[1;31m"
CBLUE  = "\033[1;34m"
CWHITE = "\033[1;37m"
CNONE  = "\033[0m"

winner_by_map    = OrderedDict()
winner_by_server = OrderedDict()
winner_by_both   = {}

class Winner:
    def __init__(self):
        self.total = 0
        self.red   = 0
        self.blue  = 0

    def ratio(self):
        if self.total > 0:
            return float(self.red - self.blue) / float(self.total)
        return None


total_count = 0
with open("red_vs_blue.csv",'r') as f:
    for line in f.readlines():
        fields = line.strip().split("\t")

        game_id, game_type, server_id, server_name, map_id, map_name, winner_tid = tuple(fields)

        skip = False
        if game_type != GAMETYPE:
            skip = True
        if game_id < MIN_GAMEID:
            skip = True
        for m in MAP_BLACKLIST:
            if m in map_name:
                skip = True
                break
        for s in SERVER_BLACKLIST:
            if s in server_name:
                skip = True
                break
        if skip:
            continue

        try:
            winner_team = TEAMS[int(winner_tid)]
        except:
            winner_team = None

        map_name = map_name[:60]
        server_name = server_name[:60]

        if not map_name in winner_by_map:
            winner_by_map[map_name] = Winner()
        if not server_name in winner_by_server:
            winner_by_server[server_name] = Winner()
        if not map_name in winner_by_both:
            winner_by_both[map_name] = {}
        if not server_name in winner_by_both[map_name]:
            winner_by_both[map_name][server_name] = Winner()

        total_count += 1
        winner_by_map[map_name].total += 1
        winner_by_server[server_name].total += 1
        winner_by_both[map_name][server_name].total += 1
        if winner_team == 'red':
            winner_by_map[map_name].red += 1
            winner_by_server[server_name].red += 1
            winner_by_both[map_name][server_name].red += 1
        elif winner_team == 'blue':
            winner_by_map[map_name].blue += 1
            winner_by_server[server_name].blue += 1
            winner_by_both[map_name][server_name].blue += 1

maps    = sorted(winner_by_map.items(), key=lambda x: -x[1].total)
servers = sorted(winner_by_server.items(), key=lambda x: -x[1].total)


print "Total Games:", total_count

print "Win Ratio by Map:"
for m,w in maps[:NMAPS]:
    if  w.ratio() < 0.:
        print CRED,
    elif w.ratio() > 0.:
        print CBLUE,
    else:
        print CWHITE,
    print "    %-30s - T:%6d R:%6d B:%6d  ->  %6.5f" % (m[:30], w.total, w.red, w.blue, w.ratio() ),
    print CNONE

print "Win Ratio by Server:"
for s,w in servers[:NSERVERS]:
    if  w.ratio() < 0.:
        print CRED,
    elif w.ratio() > 0.:
        print CBLUE,
    else:
        print CWHITE,
    print "    %-30s - T:%6d R:%6d B:%6d  ->  %6.5f " % (s[:30], w.total, w.red, w.blue, w.ratio() ),
    print CNONE


hGames = ROOT.TH2D("hGames", "Number of %s Games (%d total)" % (GAMETYPE.upper(), total_count), \
        NMAPS, 0, NMAPS-1, NSERVERS, 0, NSERVERS-1)
hRatio = ROOT.TH2D("hRatio", "Red-Blue %s Win Ratio" % (GAMETYPE.upper()), \
        NMAPS, 0, NMAPS-1, NSERVERS, 0, NSERVERS-1)
hRatioWeighted = ROOT.TH2D("hRatioWeighted", "Weighted Red-Blue %s Win Ratio" % (GAMETYPE.upper()), \
        NMAPS, 0, NMAPS-1, NSERVERS, 0, NSERVERS-1)

taken = []
for map_name in [ m for m,w in maps[:NMAPS] ]:
    for server_name in [ s for s,w in servers[:NSERVERS] ]:
        try:
            w = winner_by_both[map_name][server_name]
        except:
            continue
        key = (map_name, server_name)
        if key in taken:
            print "Skipping map: %s server: %s" % (map_name,server_name)
            continue
        taken.append( key )
        hGames.Fill( map_name, server_name, w.total )
        hRatio.Fill( map_name, server_name, w.ratio() )
        hRatioWeighted.Fill( map_name, server_name, w.ratio() * w.total/total_count )


ROOT.gStyle.SetOptStat(0)

gradientR = array('d', [ 1.00, 0.00, 1.00 ])
gradientG = array('d', [ 1.00, 1.00, 0.00 ])
gradientB = array('d', [ 1.00, 0.00, 0.00 ])
gradientS = array('d', [ 0.00, 0.50, 1.00 ])
ROOT.TColor.CreateGradientColorTable(len(gradientS), gradientS, gradientR, gradientG, gradientB, NCOLORS)

cGames = ROOT.TCanvas("cGames", "", CANVAS_SIZE, CANVAS_SIZE)
cGames.cd()
cGames.SetLogz()
pGames = cGames.GetPad(0)
pGames.SetLeftMargin(0.25)
pGames.SetRightMargin(0.1)
pGames.SetBottomMargin(0.25)
pGames.SetTopMargin(0.1)
hGames.SetContour(NCOLORS)
hGames.Draw("COLZ")
hGames.GetXaxis().SetLabelSize(0.015)
hGames.GetYaxis().SetLabelSize(0.015)
hGames.GetZaxis().SetLabelSize(0.03)
hGames.GetXaxis().LabelsOption("v")
hGames.GetYaxis().LabelsOption("h")
cGames.Modified()
cGames.Update()
cGames.SaveAs("red_vs_blue-%s_games.root" % GAMETYPE)
cGames.SaveAs("red_vs_blue-%s_games.png" % GAMETYPE)

gradientR = array('d', [ 1.00, 1.00, 0.00 ])
gradientG = array('d', [ 0.00, 1.00, 0.00 ])
gradientB = array('d', [ 0.00, 1.00, 1.00 ])
gradientS = array('d', [ 0.00, 0.50, 1.00 ])
ROOT.TColor.CreateGradientColorTable(len(gradientS), gradientS, gradientR, gradientG, gradientB, NCOLORS)

cRatio = ROOT.TCanvas("cRatio", "", CANVAS_SIZE, CANVAS_SIZE)
cRatio.cd()
#cRatio.SetLogz()
pRatio = cRatio.GetPad(0)
pRatio.SetLeftMargin(0.25)
pRatio.SetRightMargin(0.1)
pRatio.SetBottomMargin(0.25)
pRatio.SetTopMargin(0.1)
hRatio.SetContour(NCOLORS)
hRatio.Draw("COLZ")
hRatio.GetXaxis().SetLabelSize(0.015)
hRatio.GetYaxis().SetLabelSize(0.015)
hRatio.GetZaxis().SetLabelSize(0.03)
hRatio.GetXaxis().LabelsOption("v")
hRatio.GetYaxis().LabelsOption("h")
zrange = max(abs(hRatio.GetMinimum()), abs(hRatio.GetMaximum()))
hRatio.GetZaxis().SetRangeUser(-zrange,zrange)
cRatio.Modified()
cRatio.Update()
cRatio.SaveAs("red_vs_blue-%s_ratio.root" % GAMETYPE)
cRatio.SaveAs("red_vs_blue-%s_ratio.png" % GAMETYPE)

gradientR = array('d', [ 1.00, 1.00, 1.00, 0.00, 0.00 ])
gradientG = array('d', [ 1.00, 0.00, 1.00, 0.00, 1.00 ])
gradientB = array('d', [ 0.00, 0.00, 1.00, 1.00, 1.00 ])
gradientS = array('d', [ 0.00, 0.30, 0.50, 0.70, 1.00 ])
ROOT.TColor.CreateGradientColorTable(len(gradientS), gradientS, gradientR, gradientG, gradientB, NCOLORS)

cRatioWeighted = ROOT.TCanvas("cRatioWeighted", "", CANVAS_SIZE, CANVAS_SIZE)
cRatioWeighted.cd()
#cRatioWeighted.SetLogz()
pRatioWeighted = cRatioWeighted.GetPad(0)
pRatioWeighted.SetLeftMargin(0.25)
pRatioWeighted.SetRightMargin(0.1)
pRatioWeighted.SetBottomMargin(0.25)
pRatioWeighted.SetTopMargin(0.1)
hRatioWeighted.SetContour(NCOLORS)
hRatioWeighted.Draw("COLZ")
hRatioWeighted.GetXaxis().SetLabelSize(0.015)
hRatioWeighted.GetYaxis().SetLabelSize(0.015)
hRatioWeighted.GetZaxis().SetLabelSize(0.015)
hRatioWeighted.GetXaxis().LabelsOption("v")
hRatioWeighted.GetYaxis().LabelsOption("h")
zrange = max(abs(hRatioWeighted.GetMinimum()), abs(hRatioWeighted.GetMaximum()))
hRatioWeighted.GetZaxis().SetRangeUser(-zrange,zrange)
cRatioWeighted.Modified()
cRatioWeighted.Update()
cRatioWeighted.SaveAs("red_vs_blue-%s_ratio_weighted.root" % GAMETYPE)
cRatioWeighted.SaveAs("red_vs_blue-%s_ratio_weighted.png" % GAMETYPE)
[Image: 9216.png] Web: YouTubeSoundCloudFlickrzykure.de[unconnected]
IRC: #uc.xonotic #xonotic #xonotic.de #xonotic.pickup
Reply

#2
<Samual> zykure|sick: graph is useless
Reply

#3
Very interesting indeed, thanls for sharing.

It seems that color is less of an issue in higher skilled matches in contrast to what the article puropose, perhaps cause Xonotic allow forced player colors? (just looked very roughly on ucprivate collumn)
Reply

#4
I hope KH stats don't include games with yellow and pink teams! Wink

To me it's just a random fact, cause there are so many things that influence it (for public at least) like skill gaps, teamsize etc.
Also for sure the color won't change the outcome of a TDM game if a red team wins by ~20 frags and the match gets replayed with swaped colors afterwards.

If I am bored enough soon, I will go through my 5v5 CTF pickups, which are almost 200 games.

@zykure: Is it possible to check red/blue team wins for a single player? I am interested for myself in CTF, because I always try to join blue (any gamemode) -- so I bet blue got a higher win ratio there. >Big Grin

It's probably just a placebo, like I prefer to be in blue team and shooting reds. I only force a green color in DM modes.
Reply

#5
(02-11-2014, 06:20 PM)Mirio Wrote: To me it's just a random fact, cause there are so many things that influence it (for public at least) like skill gaps, teamsize etc.

Yes it probably is just some perceived effect... but then, that's what statistics are for, to even out all the actual differences of skill etc. in each match. We definitely need more games played I guess Wink


(02-11-2014, 06:20 PM)Mirio Wrote: @zykure: Is it possible to check red/blue team wins for a single player? I am interested for myself in CTF, because I always try to join blue (any gamemode) -- so I bet blue got a higher win ratio there. >Big Grin

Haha yes, pls check if that'S true! Tongue But you gotta ask Antibody to get you the gamestats table for all games where you participated. In my CSV data I don't even have player info...


(02-11-2014, 06:20 PM)Mirio Wrote: It's probably just a placebo, like I prefer to be in blue team and shooting reds. I only force a green color in DM modes.

So, you're affected by this psychological red-blue effect then? Tongue
And why don't you just for ce red color in team games, if you like shooting reds? =)
[Image: 9216.png] Web: YouTubeSoundCloudFlickrzykure.de[unconnected]
IRC: #uc.xonotic #xonotic #xonotic.de #xonotic.pickup
Reply

#6
I also like my blue teammates!

You probably need very even teams and let them play like 10 matches on the same map against each other. 5 as red, 5 as blue.

I remember these TDM games with 2 wins for red and 1 for blue:

http://stats.xonotic.org/game/184030
http://stats.xonotic.org/game/184043
http://stats.xonotic.org/game/184057

So the red color made them win those super close games (overtime even) Tongue
This is why this red/blue comparison needs many, many games under same conditions and different maps. For example cloud9 is just too good on Dreadful Place.
Reply

#7
(02-12-2014, 04:52 AM)Mirio Wrote: You probably need very even teams and let them play like 10 matches on the same map against each other. 5 as red, 5 as blue.

Actually you'll have to play a lot more than 10 matches, on different days, with randomized (but equally skilled) teams...

But yeah, it's just an interesting thing to look at. I'm not saying that it has any relevance Big Grin


Also, wtf is cloud9?! And why do you choose a silly name like that. It is especially silly compared to a totally sane name like OHSNAP! Tongue
[Image: 9216.png] Web: YouTubeSoundCloudFlickrzykure.de[unconnected]
IRC: #uc.xonotic #xonotic #xonotic.de #xonotic.pickup
Reply

#8
I confirm trying to get into blue team, feels better to shoot on red (evil guys). And I do force player model colors to green.
Reply

#9
Well everyone knows that red is faster.




http://www.spiegel.de/international/zeit...70918.html

But i think you can always make the statictics to your liking and its more a placebo than anyting else.
How to Englich?
Reply



Possibly Related Threads…
Thread Author Replies Views Last Post
  Dedicated map repo and video hosting site... end user 28 32,073 07-13-2014, 05:04 PM
Last Post: end user
Smile TeamSpeak and Mumble Server sponsored by [unconnected] jack 3 6,929 06-28-2013, 02:52 PM
Last Post: N1mbus
  New American server up and running! Lee_Stricklin 0 3,335 12-30-2011, 10:49 PM
Last Post: Lee_Stricklin
  Attention all server owners and web designers!!! Samual 39 39,964 12-15-2011, 02:34 PM
Last Post: mand1nga

Forum Jump:


Users browsing this thread:
1 Guest(s)

Forum software by © MyBB original theme © iAndrew 2016, remixed by -z-