Elo ratings, treating pairs of submissions as games.
I'm using the Elo system here.
Essentially, the solution is find the best solutions in each language to each challenge, and then treat each pair of such solutions as a match between those two languages, with the winner being the shorter submission.
Languages with less than 10 entries are discarded, and languages have a starting rating of 1000.
One drawback of this system is that ELO favors more recent entries. To compensate, I used a relatively low K
value, which makes updates slower, and so old answer count for more.
The data is from this query, courtesy of Alex A. To run the code, put the two code blocks below in files with the names given, put the query csv file in ratings.csv, and run parse.py
New results, with bug fix
In the old version of the code, there were two bugs:
I have fixed both bugs. In doing so, the code now considers differently named versions of a language (e.g. Python 2.7 and Python 2) to be different languages. I don't think this is a major problem - allowing languages with numbers in the name is much more important.
Also, since many more answers are correctly parsed, more languages meet the threshold to be displayed.
December 2023 Results:
Thunno 2: 2049
Vyxal: 2046
Nibbles: 2034
Nekomata: 2031
Husk: 1927
Jelly: 1925
SOGL V0.12: 1916
Neim: 1893
Stax: 1877
MathGolf: 1851
05AB1E: 1844
Pyke: 1833
MATL: 1811
Canvas: 1800
Japt v2.0a0: 1796
Japt: 1763
Brachylog: 1725
Ohm: 1720
Gaia: 1712
Pyt: 1708
Ohm v2: 1707
Actually: 1701
Pyth: 1658
Convex: 1645
Jolf: 1632
Arn: 1627
Uiua: 1618
Seriously: 1584
Pushy: 1553
BQN: 1548
Dyalog APL: 1544
Burlesque: 1543
APL: 1535
Pip: 1525
Keg: 1507
CJam: 1503
Charcoal: 1477
cQuents: 1476
J: 1473
V: 1459
TeaScript: 1442
Haskell + hgl: 1439
k: 1413
x86 Machine Code: 1396
𝔼𝕊𝕄𝕚𝕟: 1373
x86 32: 1373
x86: 1340
Attache: 1337
K4: 1335
Braingolf: 1317
Ly: 1314
stacked: 1313
Golfscript: 1301
Add++: 1290
Labyrinth: 1269
q/kdb+: 1234
Aceto: 1232
APL+WIN: 1223
Raku: 1201
Jellyfish: 1187
TI: 1180
DC: 1179
Q: 1177
MY: 1166
Fourier: 1138
Regex: 1131
Factor: 1120
Julia 0.6: 1117
Runic Enchantments: 1116
Octave: 1110
Proton: 1091
Cubix: 1088
ARBLE: 1086
PARI/GP: 1077
Matlab/Octave: 1063
Perl: 1057
Wolfram Language: 1053
R: 1047
QBIC: 1047
Octave / Matlab: 1046
Ruby: 1043
bash+coreutils: 1042
Julia 1.0: 1031
Bubblegum: 1030
Julia: 1028
Vim: 1027
Mathematica: 1021
Curry: 1019
MAWP: 1016
Cheddar: 1010
SageMath: 990
jq: 987
Perl 5: 987
Retina: 978
Hexagony: 964
befunge: 960
Arturo: 949
Röda: 942
JavaScript: 930
CoffeeScript: 923
Perl 6: 915
Haskell: 913
Alice: 910
Postscript: 903
GNU sed: 901
PowerShell Core: 898
Desmos: 898
Python NumPy: 892
jq 1.5: 887
Funky: 871
Groovy: 863
Retina 0.8.2: 862
sed: 860
Matlab: 856
Python 2: 854
Zsh: 853
Bash: 847
CHIP: 846
PowerShell: 844
Python: 835
Clojure: 834
Python 3.5: 832
PHP: 817
Python 3.8: 810
Mouse: 805
Cubically: 796
Java: 794
Knight: 790
QBasic: 781
SmileBASIC: 780
Excel: 776
ES6: 775
Lexurgy: 771
Python 3.6: 758
Google Sheets: 748
Elixir: 740
Processing: 732
AWK: 732
PowerShell v2+: 716
Excel VBA: 712
Scala 3: 704
MMIX: 703
Python 3: 683
JavaScript ES6: 681
Prolog: 667
Java 8: 666
Forth: 664
Icon: 650
Kotlin: 647
Red: 641
Swift 4: 636
Clean: 635
REXX: 632
Ink: 614
Batch: 603
C: 596
8th: 595
T: 585
SpecBAS: 585
Java 11: 585
C#: 585
Scala: 574
Swift: 572
Windows Batch: 566
Python 2.7: 566
Rust: 561
Lua: 557
naz: 553
Dart: 546
Yabasic: 543
Common Lisp: 522
Brainfuck: 521
tinylisp: 504
Fortran: 503
Tcl: 502
Java 7: 488
F#: 482
Nim: 480
TSQL: 479
VBA: 476
Java 10: 476
Pepe: 465
Swift 3: 441
Oracle SQL: 428
C++14: 425
Racket: 422
SWI: 416
D: 412
SNOBOL4: 393
Go: 363
Brain: 354
C++: 353
Oracle SQL 11.2: 350
Scratch: 326
Whitespace: 294
Rockstar: 294
Pascal: 283
Deadfish~: 108
Taxi: 58
Shakespeare Programming Language: 16
Editors note: The cutoff was set to 40
when generating the list, as it was the smallest cutoff (that was a multiple of 5) that didn't give any false positives like languages named 46 bytes
.
Code:
elo.py:
STARTING = 1000
SCALE = 400
K = 10
PAD = 50
import itertools
def concat(x):
return list(itertools.chain.from_iterable(x))
class Ratings:
def __init__(self, challenges):
langs = concat(challenges)
self.ratings = {lang: STARTING for lang in langs}
for challenge in challenges:
self.update_challenge(challenge)
def update_game(self, first, second, score):
f_rat = self.ratings[first]
s_rat = self.ratings[second]
f_exp = 1/(1 + 10 ** ((s_rat - f_rat) / 400))
s_exp = 1/(1 + 10 ** ((f_rat - s_rat) / 400))
f_upd = K * (score - f_exp)
s_upd = K * ((1 - score) - s_exp)
self.ratings[first] += f_upd
self.ratings[second] += s_upd
def update_challenge(self, scores):
for lang1 in sorted(scores):
for lang2 in sorted(scores):
if lang1 > lang2:
s1 = scores[lang1]
s2 = scores[lang2]
result = 1 if s1 < s2 else 0.5 if s1 == s2 else 0
self.update_game(lang1, lang2, result)
def __str__(self):
return '\n'.join(key + ':' +
' ' * (max(len(lang) + 1 for lang in self.ratings) - len(key)) + str(val)
for key, val in sorted(((lang, int(self.ratings[lang]))
for lang in self.ratings),
key=lambda x:x[::-1], reverse=True))
parse.py:
import csv
import re
import elo
CUTOFF = 40 # change as needed
lines = []
scores = {}
challenges = []
lang_counts = {}
canon_to_actual = {}
with open("merged.csv", encoding="utf-8") as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
lines.append((row["FirstLine"], row["ParentId"]))
count = 0
for line, challenge in lines:
line = re.sub("<s(trike)?>.*</s(trike)?>", "", line)
line = re.sub("<a[^>]*>", "", line)
line = re.sub("</a>", "", line)
match = re.match("<.*>(.*)</.*>", line)
if match:
line = match.group(1)
lang = re.match("([^-,\(:]*)", line)
if lang:
lang = lang.group(1).strip()
canon = lang.lower().replace(" ", "")
canon_to_actual[canon] = lang
score = re.findall(r"(\d+(?:\.\d+)?)[^0-9]+bytes?", line)
if score:
score = score[-1]
score = eval(score.lstrip("0") or "0")
c = scores.get(challenge, [])
c.append((canon, score))
scores[challenge] = c
lang_counts[canon] = lang_counts.get(canon, 0) + 1
for c in sorted(scores, key=int):
results = scores[c]
c_map = {}
for canon, score in results:
if lang_counts[canon] >= CUTOFF and re.match("\w", canon):
lang = canon_to_actual[canon]
old_score = c_map.get(lang, score)
c_map[lang] = min(score, old_score)
challenges.append(c_map)
print(elo.Ratings(challenges))
Here are the original ratings, generated with the buggy code, for posterity.
2016 Results
Jelly: 1906
Pyth: 1711
GS: 1674
Japt: 1653
MATL: 1607
Dyalog APL: 1580
J: 1579
CJam: 1578
Jolf: 1559
Seriously: 1535
TeaScript: 1499
GolfScript: 1497
Pip: 1478
Burlesque: 1460
APL: 1443
O: 1427
Vitsy: 1424
𝔼𝕊𝕄𝕚𝕟: 1384
Vim: 1371
q: 1355
K: 1322
TI: 1301
sh: 1294
Octave: 1289
PARI/GP: 1272
dc: 1269
GNU sed: 1229
Retina: 1213
x: 1209
Bash + coreutils: 1207
Bash + GNU utilities: 1201
bc: 1189
golflua: 1187
Sed: 1182
HTML: 1168
Julia: 1165
QBasic: 1156
Sage: 1146
Ruby: 1124
AWK: 1101
ZSH: 1086
Mathematica: 1083
Gema: 1079
Bash: 1079
jq: 1060
Haskell: 1051
MATLAB: 1042
PowerShell v: 1028
Perl: 1028
BBC BASIC: 1022
Piet: 1019
Score: 1018
Shell script: 1017
Mumps: 1015
Languages: 1013
ECMAScript: 1012
Regex: 1000
LiveScript: 993
Tcl: 989
CoffeeScript: 988
Groovy: 986
Postscript: 984
Windows PowerShell: 981
Powershell: 966
R: 964
Befunge: 964
gawk: 951
AutoHotkey: 934
Javascript ES: 931
Smalltalk: 928
STATA: 926
Matlab/Octave: 926
REBOL: 923
SAS: 917
Clojure: 913
ES: 909
PHP: 908
Dart: 908
Python: 894
JavaScript: 894
Node.js: 893
F#: 880
Marbelous: 875
Excel VBA: 858
AutoIt: 853
Shell: 850
Common Lisp: 845
Brainfuck: 843
Lua: 835
Processing: 832
Scheme: 824
CSS: 823
Pure bash: 821
SWI: 786
Game Maker Language: 785
FORTH: 778
SpecBAS: 776
Scala: 772
Cobra: 769
HTML + JavaScript: 758
Whitespace: 750
Applescript: 739
T: 724
Batch: 720
Fortran: 715
Prolog: 711
Turing Machine Code: 708
C: 706
SQL: 704
VBA: 703
Factor: 700
Kotlin: 690
JS: 686
Pascal: 685
Erlang: 674
Emacs Lisp: 673
OCaml: 668
Racket: 658
VBScript: 654
VB.NET: 643
Swift: 636
D: 621
Windows Batch: 612
Ceylon: 591
C++: 587
Java: 559
Oracle SQL: 548
Delphi: 539
Go: 537
C#: 535
Rust: 528
"Hello, World!"
(with or without the trailing"
) is a valid program in dozens of languages. Or ones where a particular language has a clear built-in that leapfrogs it ahead of where it would "normally" sit. Challenges like that would skew the numbers incorrectly. Not that I'm against the idea, it just needs a lot of rigor. \$\endgroup\$