Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Leetcode contests #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
152334H wants to merge 41 commits into clearloop:master from 152334H:master
Closed

Leetcode contests #69

152334H wants to merge 41 commits into clearloop:master from 152334H:master

Conversation

Copy link
Contributor

@152334H 152334H commented May 31, 2022
edited
Loading

TL;DR

You can play leetcode contests on this fork. I played last weekend's contests with this; see the User Experience example.

I want feedback for anything and everything -- code smells, CLI design, even "this sucks and I'm rejecting it".

New feature: Playing with contests in leetcode-cli

This is a short overview of the work I've done to get leetcode contests working in this app. First, I cover the client-server relationship for leetcode contests. Then, I briefly explain the architectural decisions I made in implementation. Finally, I demonstrate what usage of the new leetcode contest command looks like, and go over possible changes.

The git commit history for this PR is dirty; it might be easier to see the files changed here.

Map of the Leetcode contest API

Contests are stored in the leetcode backend as the ContestNode type (see next section), which mostly corresponds to the information available at leetcode.com/contest/$slug. We can retrieve information by querying

  • sending a GET request to https://leetcode.com/contest/api/info/$contest_slug, or
  • querying the graphql API for { contest(titleSlug: String!) }

Before a contest begins, users must register for the contest to participate in it. This can be done with a simple empty POST request to

  • https://leetcode.com/contest/api/$contest_slug/register

Once a contest starts, the client needs to make additional requests to the leetcode API to get information about the contest problems. For normal leetcode.com users, the information appears to be dumped directly into HTML inside a <script> tag. I did not want to implement/import a full blown HTML parser || use a horrible string/regex search hack, so I found an alternative:

  • Contest problems can be queried from the graphql API like normal problems (i.e. both are accessible via { question(titleSlug: String!) } queries). There are some differences between the data returned for a contest problem and a normal problem, and they will need to be handled later in the CLI's code.

After the user finishes implementing the solution for a problem, they need to submit their code to be judged by the leetcode runtime. A dedicated contest API submission route must be used; running code via the normal API routes "works" but doesn't count for gaining contest points.

  • https://leetcode.com/contest/api/$contest/problems/$slug/interpret_solution/
  • https://leetcode.com/contest/api/$contest/problems/$slug/submit/

Users might also want to check the contest scoreboard to see their position. I have not worked on querying/implementing this yet.

Interlude: what's what the fun command?

Leetcode's API is not documented (at all). I discovered all of the information in the section above by a mixture of the Firefox Dev Console && unsolicited queries to leetcode.com/graphql. The latter is what the fun subcommand is for; I made it as a quick debugging tool to enumerate leetcode's graphql API.

As an example, you can get the structure of a ContestQuestionNode like this:

$ leetcode f -t ContestQuestionNode | jq .
 "data": {
 "__type": {
 "name": "ContestQuestionNode",
 "fields": [
 {
 "name": "credit",
 "type": {
 "name": null,
 "kind": "NON_NULL",
 "ofType": {
 "name": "Int",
 "kind": "SCALAR"
 }
 }
 },
 {
 "name": "title",
 "type": {
 "name": "String",
 "kind": "SCALAR",
 "ofType": null
 }
 },
 {
 "name": "titleSlug",
 "type": {
 "name": "String",
 "kind": "SCALAR",
 "ofType": null
 }
 },
 {
 "name": "questionId",
 "type": {
 "name": "String",
 "kind": "SCALAR",
 "ofType": null
 }
 }
 ]
 }
 }

None of this is needed for a normal user of leetcode-cli, of course, so I will probably remove it unless you think it would be a good idea to keep it.

IMPLEMENTATION

So, how does the API translate to code?

We need some way to expose the following operations to the user:
1, get contest info (given a slug, like"weekly-contest-295")
2. register for a contest
3. get contset problem info
4. submit code to test/run on contest problems

Because contest problems are structurally identical to normal leetcode problems, (4) is actually already solved -- the code for the test/exec commands can be used here, with a little modification. (3) is also mostly solved by the existing code, but there are a few issues:

  • The existing code relies on the normal problem url (conf.sys.urls["problem"]) to read problem descriptions, so I added a longer graphql query (Leetcode::get_contest_question_detail) for that
  • The QuestionNode data from leetcode for each problem will be slightly modified after a contest ends && the contest problems are republished as normal problems. This causes a user's code for a contest problem to "disappear" from leetcode-cli after a contest, because the frontendQuestionId for each problem changes && the user's code file in ~/.leetcode/code is no longer tracked properly by leetcode-cli. I have some ideas to handle this, but I haven't done anything about it yet.

That leaves (2) and (1). I added the Contest and ContestQuestionStub structs to models.rs; they represent the ContestNode and ContestQuestionNode types from the leetcode graphql API. Corresponding methods were added in leetcode,rs, cache/mod.rs, and parser.rs.

The data for the contest structs could (and should) easily be cached, but for the time being I have only implemented direct queries for these structs from the leetcode API.

I've also made a substantial number of changes to existing bodies of code, so it's entirely possible I've accidentally broken a feature or two at some point. I've tried to add TODOs to places where I think the code will probably need to change, but there is probably more.

UX

Currently, the end-user experience with leetcode-cli looks like this:

$ leetcode contest weekly-contest-295 -ru
started 1 seconds ago
[weekly-contest-295] Weekly Contest 295
fID Points Title
------|------|----------------------
2372 | 3 | Rearrange Characters to Make Target String
2373 | 4 | Apply Discount to Prices
2374 | 5 | Steps to Make Array Non-decreasing
2375 | 6 | Minimum Obstacle Removal to Reach Corner
$ leetcode e 2372 # work on the problem
$ leetcode t -c weekly-contest-295 2372 # test solution
Accepted Runtime: 35 ms
Your input: "ilovecodingonleetcode""code"
Output: 2
Expected: 2
$ leetcode x -c weekly-contest-295 2372 # exec solution
Success
Runtime: 64 ms, faster than 11% of Python3 online submissions for Rearrange Characters to Make Target String.
Memory Usage: 13.7 MB, less than 98% of Python3 Rearrange Characters to Make Target String.

This system works well enough, but it could definitely be a lot more ergonomic. I was hoping to implement an ncurses-like interface for contests, but

  • Because of how the other subcommands are implemented, a significant amount of refactoring would be needed to edit/execute code without recursively calling leetcode-cli as a subprocess
  • Adding an interactive UI would cost a lot more lines of code & maintenance. Ncurses in particular (and its rust wrapper, pancake) is not very good at the thread safety / async thing.

Nonetheless, the current output of the contest command is rather ugly, and more work ought to be done here.

End

hao-lee, NetWilliam, wendajiang, Congee, j178, curtainp, and rockyzhang24 reacted with thumbs up emoji clearloop and wq-yang reacted with rocket emoji
152334H added 30 commits May 18, 2022 15:35
...entication, use `is_session_bad()` to check if LEETCODE_SESSION is valid.
...use `function_name!()` instead of manually writing function names
@152334H 152334H marked this pull request as draft May 31, 2022 03:25
Copy link
Owner

hey @152334H ! really appreciate ur awesome work! will you continue on this or need some help?

Copy link
Contributor Author

152334H commented Jun 26, 2023

i suggest closing it, i really have no time to touch this anymore

Copy link
Contributor Author

152334H commented Jun 26, 2023

also i think it broke at least 3 months ago

Copy link
Owner

clearloop commented Jun 26, 2023
edited
Loading

i suggest closing it, i really have no time to touch this anymore

haha understood! thank you for your contribution! It's been a long time for me as well having no time contributing to the open source projects, I'll take over this and continue your work! will ask for your reviews once I complete it, is that okay for you?

Copy link
Contributor Author

152334H commented Jun 26, 2023

i mean sure I can look at it, but there's not much i'll remember from last year

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

AltStyle によって変換されたページ (->オリジナル) /