Skip to content

Commit 883045a

Browse files
author
DenjellBoone
authored
Develop duelnotags (cheran-senthil#498)
* ;gimme supports tags that should not be part of the problem * ;gimme supports tags that should not be part of the problem * Added notag behaviour to ;gimme ;duel challenge ;mashup and ;stalk * Bugfix mashup and fixed some usage descriptions * Renamed notags to bantags * Changes due to PR comments * Changes due to PR comments * Changes due to PR comments
1 parent 9817317 commit 883045a

File tree

4 files changed

+34
-20
lines changed

4 files changed

+34
-20
lines changed

tle/cogs/codeforces.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,13 @@ async def upsolve(self, ctx, choice: int = -1):
9696
await ctx.send(embed=embed)
9797

9898
@commands.command(brief='Recommend a problem',
99-
usage='[tags...] [rating]')
99+
usage='[+tag..] [~tag..] [rating]')
100100
@cf_common.user_guard(group='gitgud')
101101
async def gimme(self, ctx, *args):
102102
handle, = await cf_common.resolve_handles(ctx, self.converter, ('!' + str(ctx.author),))
103103
rating = round(cf_common.user_db.fetch_cf_user(handle).effective_rating, -2)
104-
tags = cf_common.parse_tags(args)
104+
tags = cf_common.parse_tags(args, prefix='+')
105+
bantags = cf_common.parse_tags(args, prefix='~')
105106
rating = cf_common.parse_rating(args, rating)
106107

107108
submissions = await cf.user.status(handle=handle)
@@ -110,7 +111,8 @@ async def gimme(self, ctx, *args):
110111
problems = [prob for prob in cf_common.cache2.problem_cache.problems
111112
if prob.rating == rating and prob.name not in solved
112113
and not cf_common.is_contest_writer(prob.contestId, handle)
113-
and prob.matches_all_tags(tags)]
114+
and prob.matches_all_tags(tags)
115+
and not prob.matches_any_tag(bantags)]
114116

115117
if not problems:
116118
raise CodeforcesCogError('Problems not found within the search parameters')
@@ -131,7 +133,7 @@ async def gimme(self, ctx, *args):
131133
await ctx.send(f'Recommended problem for `{handle}`', embed=embed)
132134

133135
@commands.command(brief='List solved problems',
134-
usage='[handles] [+hardest] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
136+
usage='[handles] [+hardest] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [~tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
135137
async def stalk(self, ctx, *args):
136138
"""Print problems solved by user sorted by time (default) or rating.
137139
All submission types are included by default (practice, contest, etc.)
@@ -169,13 +171,15 @@ def make_page(chunk):
169171
pages = [make_page(chunk) for chunk in paginator.chunkify(submissions[:100], 10)]
170172
paginator.paginate(self.bot, ctx.channel, pages, wait_time=5 * 60, set_pagenum_footers=True)
171173

172-
@commands.command(brief='Create a mashup', usage='[handles] [+tags]')
174+
@commands.command(brief='Create a mashup', usage='[handles] [+tag..] [~tag..]')
173175
async def mashup(self, ctx, *args):
174176
"""Create a mashup contest using problems within +-100 of average rating of handles provided.
175177
Add tags with "+" before them.
178+
Ban tags with "~" before them.
176179
"""
177-
handles = [arg for arg in args if arg[0] != '+']
178-
tags = cf_common.parse_tags(args)
180+
handles = [arg for arg in args if arg[0] not in '+~']
181+
tags = cf_common.parse_tags(args, prefix='+')
182+
bantags = cf_common.parse_tags(args, prefix='~')
179183

180184
handles = handles or ('!' + str(ctx.author),)
181185
handles = await cf_common.resolve_handles(ctx, self.converter, handles)
@@ -188,7 +192,8 @@ async def mashup(self, ctx, *args):
188192
if abs(prob.rating - rating) <= 100 and prob.name not in solved
189193
and not any(cf_common.is_contest_writer(prob.contestId, handle) for handle in handles)
190194
and not cf_common.is_nonstandard_problem(prob)
191-
and prob.matches_all_tags(tags)]
195+
and prob.matches_all_tags(tags)
196+
and not prob.matches_any_tag(bantags)]
192197

193198
if len(problems) < 4:
194199
raise CodeforcesCogError('Problems not found within the search parameters')

tle/cogs/duel.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,9 +129,10 @@ async def selfregister(self, ctx):
129129
f'{ctx.author.mention} is already a registered duelist')
130130
await ctx.send(f'{ctx.author.mention} successfully registered as a duelist')
131131

132-
@duel.command(brief='Challenge to a duel', usage='opponent [rating] [+tags]')
132+
@duel.command(brief='Challenge to a duel', usage='opponent [rating] [+tag..] [~tag..]')
133133
async def challenge(self, ctx, opponent: discord.Member, *args):
134-
"""Challenge another server member to a duel. Problem difficulty will be the lesser of duelist ratings minus 400. You can alternatively specify a different rating. The duel will be unrated if specified rating is above the default value or tags are used to choose a problem. The challenge expires if ignored for 5 minutes."""
134+
"""Challenge another server member to a duel. Problem difficulty will be the lesser of duelist ratings minus 400. You can alternatively specify a different rating.
135+
The duel will be unrated if specified rating is above the default value or tags are used to choose a problem. The challenge expires if ignored for 5 minutes."""
135136
challenger_id = ctx.author.id
136137
challengee_id = opponent.id
137138

@@ -157,14 +158,15 @@ async def challenge(self, ctx, opponent: discord.Member, *args):
157158
raise DuelCogError(
158159
f'{opponent.mention} is currently in a duel!')
159160

160-
tags = cf_common.parse_tags(args)
161+
tags = cf_common.parse_tags(args, prefix='+')
162+
bantags = cf_common.parse_tags(args, prefix='~')
161163
rating = cf_common.parse_rating(args)
162164
users = [cf_common.user_db.fetch_cf_user(handle) for handle in handles]
163165
lowest_rating = min(user.rating or 0 for user in users)
164166
suggested_rating = max(
165167
round(lowest_rating, -2) + _DUEL_RATING_DELTA, 500)
166168
rating = round(rating, -2) if rating else suggested_rating
167-
unofficial = rating > suggested_rating or len(tags) > 0
169+
unofficial = rating > suggested_rating or tags or bantags
168170
dtype = DuelType.UNOFFICIAL if unofficial else DuelType.OFFICIAL
169171

170172
solved = {
@@ -177,7 +179,8 @@ def get_problems(rating):
177179
if prob.rating == rating and prob.name not in solved and prob.name not in seen
178180
and not any(cf_common.is_contest_writer(prob.contestId, handle) for handle in handles)
179181
and not cf_common.is_nonstandard_problem(prob)
180-
and prob.matches_all_tags(tags)]
182+
and prob.matches_all_tags(tags)
183+
and not prob.matches_any_tag(bantags)]
181184

182185
for problems in map(get_problems, range(rating, 400, -100)):
183186
if problems:

tle/cogs/graphs.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ async def extreme(self, ctx, *args: str):
326326
await ctx.send(embed=embed, file=discord_file)
327327

328328
@plot.command(brief="Show histogram of solved problems' rating on CF",
329-
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
329+
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [~tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
330330
async def solved(self, ctx, *args: str):
331331
"""Shows a histogram of solved problems' rating on Codeforces for the handles provided.
332332
e.g. ;plot solved meooow +contest +virtual +outof +dp"""
@@ -378,7 +378,7 @@ async def solved(self, ctx, *args: str):
378378
await ctx.send(embed=embed, file=discord_file)
379379

380380
@plot.command(brief='Show histogram of solved problems on CF over time',
381-
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [phase_days=] [c+marker..] [i+index..]')
381+
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [~tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [phase_days=] [c+marker..] [i+index..]')
382382
async def hist(self, ctx, *args: str):
383383
"""Shows the histogram of problems solved on Codeforces over time for the handles provided"""
384384
filt = cf_common.SubFilter()
@@ -460,7 +460,7 @@ async def hist(self, ctx, *args: str):
460460
await ctx.send(embed=embed, file=discord_file)
461461

462462
@plot.command(brief='Plot count of solved CF problems over time',
463-
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
463+
usage='[handles] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [~tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [c+marker..] [i+index..]')
464464
async def curve(self, ctx, *args: str):
465465
"""Plots the count of problems solved over time on Codeforces for the handles provided."""
466466
filt = cf_common.SubFilter()
@@ -497,7 +497,7 @@ async def curve(self, ctx, *args: str):
497497
await ctx.send(embed=embed, file=discord_file)
498498

499499
@plot.command(brief='Show history of problems solved by rating',
500-
aliases=['chilli'], usage='[handle] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [b=10] [s=3] [c+marker..] [i+index..] [+nolegend]')
500+
aliases=['chilli'], usage='[handle] [+practice] [+contest] [+virtual] [+outof] [+team] [+tag..] [~tag..] [r>=rating] [r<=rating] [d>=[[dd]mm]yyyy] [d<[[dd]mm]yyyy] [b=10] [s=3] [c+marker..] [i+index..] [+nolegend]')
501501
async def scatter(self, ctx, *args):
502502
"""Plot Codeforces rating overlaid on a scatter plot of problems solved.
503503
Also plots a running average of ratings of problems solved in practice."""

tle/util/codeforces_common.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ def parse_date(arg):
281281
raise ParamParseError(f'{arg} is an invalid date argument')
282282

283283

284-
def parse_tags(args):
285-
tags = [x[1:] for x in args if x[0] == '+']
284+
def parse_tags(args, *, prefix):
285+
tags = [x[1:] for x in args if x[0] == prefix]
286286
return tags
287287

288288

@@ -301,6 +301,7 @@ def __init__(self, rated=True):
301301
self.rlo, self.rhi = 500, 3800
302302
self.types = []
303303
self.tags = []
304+
self.bantags = []
304305
self.contests = []
305306
self.indices = []
306307

@@ -327,6 +328,10 @@ def parse(self, args):
327328
if len(arg) == 1:
328329
raise ParamParseError('Problem tag cannot be empty.')
329330
self.tags.append(arg[1:])
331+
elif arg[0] == '~':
332+
if len(arg) == 1:
333+
raise ParamParseError('Problem tag cannot be empty.')
334+
self.bantags.append(arg[1:])
330335
elif arg[0:2] == 'd<':
331336
self.dhi = min(self.dhi, parse_date(arg[2:]))
332337
elif arg[0:3] == 'd>=':
@@ -374,6 +379,7 @@ def filter_subs(self, submissions):
374379
type_ok = submission.author.participantType in self.types
375380
date_ok = self.dlo <= submission.creationTimeSeconds < self.dhi
376381
tag_ok = problem.matches_all_tags(self.tags)
382+
bantag_ok = not problem.matches_any_tag(self.bantags)
377383
index_ok = not self.indices or any(index.lower() == problem.index.lower() for index in self.indices)
378384
contest_ok = not self.contests or (contest and contest.matches(self.contests))
379385
team_ok = self.team or len(submission.author.members) == 1
@@ -385,7 +391,7 @@ def filter_subs(self, submissions):
385391
problem_ok = (not contest or contest.id >= cf.GYM_ID_THRESHOLD
386392
or not is_nonstandard_problem(problem))
387393
rating_ok = True
388-
if type_ok and date_ok and rating_ok and tag_ok and team_ok and problem_ok and contest_ok and index_ok:
394+
if type_ok and date_ok and rating_ok and tag_ok and bantag_ok and team_ok and problem_ok and contest_ok and index_ok:
389395
filtered_subs.append(submission)
390396
return filtered_subs
391397

0 commit comments

Comments
 (0)