clearloop / leetcode-cli

May the code be with you 👻
MIT License
317 stars 54 forks source link

Leetcode contests #69

Closed 152334H closed 9 months ago

152334H commented 2 years ago

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

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

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:

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.

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")

  1. register for a contest
  2. get contset problem info
  3. 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:

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

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

End

clearloop commented 1 year ago

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

152334H commented 1 year ago

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

152334H commented 1 year ago

also i think it broke at least 3 months ago

clearloop commented 1 year ago

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?

152334H commented 1 year ago

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