Skip to content

Commit bab55a2

Browse files
committed
feat: update contest rating
1 parent 05f64dd commit bab55a2

File tree

3 files changed

+113
-5
lines changed

3 files changed

+113
-5
lines changed

solution/CONTEST_README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99

1010
如果竞赛积分处于段位的临界值,在每周比赛结束重新计算后会出现段位升级或降级的情况。段位升级或降级后会自动替换对应的荣誉勋章。
1111

12-
| 段位 | 比例 | 段位名 | 分数线 | 勋章展示 |
12+
| 段位 | 比例 | 段位名 | 分数线(守门员) | 勋章展示 |
1313
| ----- | ------ | -------- | --------- | --------------------------------------------------------------------------- |
14-
| $LV3$ | $5\%$ | $Guardian$ | $\ge2250$ | ![](https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Guardian.gif) |
15-
| $LV2$ | $20\%$ | $Knight$ | $\ge1860$ | ![](https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Knight.gif) |
14+
| $LV3$ | $5\%$ | $Guardian$ | $\ge2216$ | <p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Guardian.gif" style="width: 100px;" /></p> |
15+
| $LV2$ | $20\%$ | $Knight$ | $\ge1863$ | <p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Knight.gif" style="width: 100px;" /></p> |
1616
| $LV1$ | $75\%$ | - | - | - |
1717

1818
## 赛后估分网站

solution/CONTEST_README_EN.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ The contest badge is calculated based on the contest rating.
99
For LeetCoders with rating >=1600,
1010
If you are in the top 5% of the contest rating, you’ll get the “Guardian” badge.
1111

12-
![](https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Guardian.gif)
12+
<p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Guardian.gif" style="width: 100px;" /></p>
1313

1414
If you are in the top 25% of the contest rating, you’ll get the “Knight” badge.
1515

16-
![](https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Knight.gif)
16+
<p><img alt="" src="https://fastly.jsdelivr.net/gh/doocs/leetcode@main/images/Knight.gif" style="width: 100px;" /></p>
1717

1818
For top 10 users (excluding LCCN users), your LeetCode ID will be colored orange on the ranking board. You'll also have the honor with you when you post/comment under discuss.
1919

solution/rating.py

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import re
2+
import sys
3+
from functools import cache
4+
5+
import requests
6+
7+
url = 'https://leetcode.cn/graphql'
8+
9+
10+
# 分页加载排名列表
11+
@cache
12+
def load_page(page):
13+
query = "{\n localRankingV2(page:" + str(
14+
page) + ") {\nmyRank {\nattendedContestCount\ncurrentRatingRanking\ndataRegion\nisDeleted\n" \
15+
"user {\nrealName\nuserAvatar\nuserSlug\n__typename\n}\n__typename\n}\npage\ntotalUsers\nuserPerPage\n" \
16+
"rankingNodes {\nattendedContestCount\ncurrentRatingRanking\ndataRegion\nisDeleted\n" \
17+
"user {\nrealName\nuserAvatar\nuserSlug\n__typename\n}\n__typename\n}\n__typename\n }\n}\n"
18+
retry = 0
19+
while retry < 3:
20+
resp = requests.post(url=url, json={'query': query})
21+
if resp.status_code == 200:
22+
nodes = resp.json()['data']['localRankingV2']['rankingNodes']
23+
return [(int(nd['currentRatingRanking']), nd['user']['userSlug']) for nd in nodes]
24+
else:
25+
retry += 1
26+
return None
27+
28+
29+
# 根据用户名获取其个人主页显示的真实分数,因为四舍五入会导致一部分 1599.xxx 的用户也显示为 1600 分
30+
@cache
31+
def get_user_rank(uid):
32+
operation_name = "userContest"
33+
query = "query userContest($userSlug: String!){\n userContestRanking(userSlug: $userSlug){" \
34+
"\ncurrentRatingRanking\nratingHistory\n}\n}\n "
35+
variables = {'userSlug': uid}
36+
retry = 0
37+
while retry < 3:
38+
resp = requests.post(url=url, json={
39+
'operationName': operation_name,
40+
'query': query,
41+
'variables': variables
42+
})
43+
if resp.status_code == 200:
44+
ranking = resp.json()['data']['userContestRanking']
45+
score = None
46+
if ranking and 'ratingHistory' in ranking:
47+
s = ranking['ratingHistory']
48+
mth = re.search(r'(\d+(?:\.\d+)?)(?:, null)*]$', s)
49+
if mth:
50+
score = mth.group(1)
51+
return (ranking['currentRatingRanking'], float(score)) if score else (None, None)
52+
else:
53+
retry += 1
54+
return None, None
55+
56+
57+
# 使用二分的方式获取1600分以上的人数,并使用 get_user_rank 方法校准
58+
def get_1600_count() -> int:
59+
l, r = 1, 1000
60+
while l < r:
61+
mid = (l + r + 1) >> 1
62+
page = load_page(mid)
63+
print(f'第 {mid} 页:', page)
64+
if not page:
65+
return 0
66+
_, score = get_user_rank(page[0][1])
67+
if score < 1600:
68+
r = mid - 1
69+
else:
70+
l = mid
71+
page = load_page(l)
72+
print('校准中...')
73+
l, r = 0, len(page)
74+
while l < r:
75+
mid = (l + r + 1) >> 1
76+
_, score = get_user_rank(page[mid][1])
77+
if score < 1600:
78+
r = mid - 1
79+
else:
80+
l = mid
81+
82+
return get_user_rank(page[l][1])[0]
83+
84+
85+
# 获取指定排名的用户
86+
@cache
87+
def get_user(rank):
88+
if rank <= 0:
89+
raise Exception('无效的排名')
90+
p = (rank - 1) // 25 + 1
91+
off = (rank - 1) % 25
92+
page = load_page(p)
93+
_, score = get_user_rank(page[off][1])
94+
return score, page[off][1]
95+
96+
97+
total = get_1600_count()
98+
if not total:
99+
print('网络故障')
100+
sys.exit()
101+
print(f'1600 分以上共计 {total} 人')
102+
103+
guardian = int(total * 0.05)
104+
knight = int(total * 0.25)
105+
g_first, g_last = get_user(1), get_user(guardian)
106+
print(f'Guardian(top 5%): 共 {guardian} 名,守门员 {g_last[0]} 分(uid: {g_last[1]}),最高 {g_first[0]} 分(uid: {g_first[1]})')
107+
k_first, k_last = get_user(guardian + 1), get_user(knight)
108+
print(f'Knight(top 25%): 共 {knight} 名,守门员 {k_last[0]} 分(uid: {k_last[1]}),最高 {k_first[0]} 分(uid: {k_first[1]})')

0 commit comments

Comments
 (0)