(edited by Snowreap.5174)
Simple C# Example - Rating Calculation
Example input (copied directly from the Leaderboard into Notepad):
1 Sanctum of Rall 2221.0640 173.5353 0.7634
2 Blackgate 2157.3918 172.9866 0.7617
3 Jade Quarry 2133.8296 172.6257 0.7609
4 Tarnished Coast 2007.7672 172.4862 0.7597
5 Dragonbrand 1962.8613 172.8228 0.7632
6 Fort Aspenwood 1960.8502 172.6732 0.7589
7 Maguuma 1823.5967 174.9570 0.7723
8 Kaineng 1782.2227 175.9198 0.7935
9 Yak's Bend 1745.0370 173.6684 0.7620
10 Sea of Sorrows 1716.8704 180.1183 0.7736
11 Crystal Desert 1586.2152 176.1478 0.7682
12 Ehmry Bay 1550.0354 182.2718 0.7824
13 Stormbluff Isle 1542.7919 177.5990 0.7761
14 Borlis Pass 1370.9421 175.9778 0.7625
15 Anvil Rock 1287.0150 178.1011 0.7599
16 Darkhaven 1272.9904 175.5942 0.7668
17 Sorrow's Furnace 1219.0934 176.3832 0.7887
18 Isle of Janthir 1215.2333 175.5854 0.7830
19 Gate of Madness 1132.9073 174.1129 0.7631
20 Devona's Rest 1063.6667 173.5354 0.7681
21 Northern Shiverpeaks 1047.4753 173.0216 0.7570
22 Henge of Denravi 1012.8758 173.1538 0.7624
23 Ferguson's Crossing 880.6989 173.3165 0.7702
24 Eredon Terrace 853.4559 173.5841 0.7701
Example output (default mode 0):
1 Sanctum of Rall 2218.4790 172.9671 0.7543 -2.5850
2 Blackgate 2150.2010 171.9685 0.7525 -7.1908
3 Jade Quarry 2143.5600 172.1516 0.7518 +9.7304
4 Dragonbrand 1994.6900 184.8914 0.7568 +31.8287 +1
5 Tarnished Coast 1981.9582 178.2444 0.7521 -25.8090 -1
6 Fort Aspenwood 1926.8598 176.7001 0.7513 -33.9904
7 Sea of Sorrows 1785.7103 188.2244 0.7687 +68.8399 +3
8 Maguuma 1758.3706 181.6671 0.7665 -65.2261 -1
9 Kaineng 1731.6037 178.2449 0.7853 -50.6190 -1
10 Yak's Bend 1731.4483 174.5728 0.7533 -13.5887 -1
11 Crystal Desert 1652.0692 179.5129 0.7621 +65.8540
12 Stormbluff Isle 1582.5558 193.7547 0.7708 +39.7639 +1
13 Ehmry Bay 1534.2980 186.1550 0.7745 -15.7374 -1
14 Borlis Pass 1371.7103 177.0362 0.7540 +0.7682
15 Anvil Rock 1300.5133 180.5061 0.7521 +13.4983
16 Darkhaven 1236.0851 179.5175 0.7593 -36.9053
17 Sorrow's Furnace 1199.3135 188.6672 0.7815 -19.7799
18 Isle of Janthir 1180.9801 184.8205 0.7758 -34.2532
19 Gate of Madness 1131.7128 174.7810 0.7543 -1.1945
20 Northern Shiverpeaks 1084.1215 176.6530 0.7495 +36.6462 +1
21 Devona's Rest 1046.5120 177.6124 0.7598 -17.1547 -1
22 Henge of Denravi 1015.3208 178.1281 0.7543 +2.4450
23 Ferguson's Crossing 932.8182 184.1240 0.7643 +52.1193
24 Eredon Terrace 869.3953 184.4527 0.7630 +15.9394
Note that the output can be saved and used as input for a future run, because it is essentially in the same format (the rating change and rank change values are ignored on input). In particular, the Mode 0 output is intended to then be used as input for Mode 1/2/3.
-ken
(edited by Snowreap.5174)
Wow, thanks for that ;-) The only thing that I would wish for in an “example” would be more comments (that was some pretty intense math being used, without an explanation of what was going on), but perhaps that’s more because I’m still new to the realm of JSON, etc, and have never used an API before in my life. Well done, though!
Oh, how standards change over time. For me, spaghetti code has always been “code with a lot of gotos”. In this code, the control flow is very straightforward: it flows from top to bottom, except for loops (which execute top to bottom).
The complaint about a lack of comments is definitely warranted — it wasn’t originally written to be an example; I made it only for my own use, and as such it has all the comments I need. If I get some time I’ll add more comments and upload again.
The one part that is hard to understand is the mode 0 code that does the actual Glicko-2 rating calculation. That part is pretty much a direct implementation of the Glicko-2 algorithm as documented here: http://www.glicko.net/glicko/glicko2.pdf.
The one bit that’s not covered in Glickman’s paper is ArenaNet’s method of calculating a score (represented by “s”), and their choice of a tau value. That information is here: http://www.guildwars2guru.com/news/884-the-math-behind-wvw-ratings/
The Glicko-2 algorithm is impossible to understand just by looking at the code, but it compares fairly directly to the algorithm as described in Glickman’s paper. The algorithm in the paper isn’t particularly easy to understand either so in that respect the code isn’t much worse.
The rest of the modes perform statistical analysis using the Monte Carlo method, where you run a large number of random simulations and count how many of each of the outcomes you get. The code for that is fairly straightforward, but assumes you know how GW2 matchups are done. That information is found here: https://www.guildwars2.com/en/news/big-changes-coming-to-wvw-matchups/.
The JSON code has no comments at all but I’ve provided it only so that the program is complete and will compile — I hadn’t really intended it to be an example of a JSON parser, although if you know the basics of parsing (i.e. the difference between token parsing and lexical analysis), it can certainly serve that purpose. The Value.GetValue() method contains the JSON parser, which consumes tokens from the Tokenizer (aka the Lexical Analyzer) via the Tokenizer.ReadToken() method. If you’ve ever had to write your own parser & lexer before, my implementation will be very clear but if you haven’t this code isn’t intended to teach you how. I suspect most people doing ‘real work’ use tools like bison and flex to auto-generate parsers and lexers, rather than writing them by hand.
-ken
(edited by Snowreap.5174)
Ok, I’ve updated the original post with a newer version of code that has more comments. But the added comments in the main program are not a substitute for understanding the underlying ideas — be sure to check these web pages for the necessary background information:
https://www.guildwars2.com/en/news/big-changes-coming-to-wvw-matchups/
http://www.guildwars2guru.com/news/884-the-math-behind-wvw-ratings/
http://www.glicko.net/glicko/glicko2.pdf
The JSON code is still relatively comment-free, aside from a reference to consult http://www.json.org/. The JSON syntax diagram shown there matches the JSON Tokenizer flow fairly well.
-ken
Nice job doing it the “man-ly” way – implementing your own parser and all. You can breakup some parts into smaller functions/objects, guilty of that myself, but otherwise it looks fine to me.
(edited by DarkSpirit.7046)