Skip to content

Commit ec10aa2

Browse files
committed
open source
1 parent 49f420d commit ec10aa2

34 files changed

+2278
-101
lines changed

README.md

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,27 @@ pip install leetcode-local-tester
2626
leetcode-local-tester work --help
2727

2828
Options:
29-
--kind TEXT The question kind. Now support: `contest`, `problem`,
30-
`season`, and `contest` includes `weekly` and `biweekly`.
31-
Default is `problem`.
32-
--detail TEXT The detail of the question. If type is `contest` or
33-
`problem`, the detail is the url. Such as
34-
`https://leetcode.com/contest/weekly-contest-326/`,
35-
`https://leetcode.con/problems/minimum-number-of-
36-
operations-to-reinitialize-a-permutation/`. If type is
37-
`season`, the detail is the season name. Such as
38-
`2020-fall-solo` or `2020-fall-team`.
39-
--language TEXT The language of the code. Now support: `cpp`, `python3`. Default is
40-
`python3`.
41-
--location TEXT The location of the code. Default is `./leetcode/`.
42-
--help Show this message and exit.
29+
--kind TEXT The question kind. Now support: `contest`, `problem`,
30+
`season`, and `contest` includes `weekly` and
31+
`biweekly`. Default is `problem`.
32+
--detail TEXT The detail of the question. If type is `contest` or
33+
`problem`, the detail is the url. Such as
34+
`https://leetcode.com/contest/weekly-contest-326/`,
35+
`https://leetcode.cn/problems/minimum-number-of-
36+
operations-to-reinitialize-a-permutation/`. If type is
37+
`season`, the detail is the season name. Such as
38+
`2020-fall-solo` or `2020-fall-team`.
39+
--language TEXT The language of the code. Now support: `cpp`,
40+
`python3`. Default is `python3`.
41+
--location TEXT The location of the code. Default is `./leetcode/`.
42+
--help Show this message and exit.
4343
```
44+
## Before you use
45+
Because the utility needs to login to Leetcode to get some information, there are two ways to login. One is to use username and password. You need to set these value to environment variables: `LEETCODE_USERNAME` and `LEETCODE_PASSWORD`. The other is to use cookie. You need to set the cookie to environment variable: `LEETCODE_COOKIE`. You can read the article [How to get the cookie](https://betterprogramming.pub/work-on-leetcode-problems-in-vs-code-5fedf1a06ca1) to get the cookie.
46+
- Note: If you use `leetcode.com`. You cannot use username and password to login, because `leetcode.com` has recaptcha. So you need to use cookie to login.
47+
48+
49+
4450

4551
## Example
4652
```bash

leetcode_local_tester/api.py

Lines changed: 0 additions & 34 deletions
This file was deleted.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from leetcode_local_tester.creator.cpp_creator import CppCreator
2+
from leetcode_local_tester.creator.python3_creator import PythonCreator
3+
4+
creator_factory = {
5+
CppCreator.code_type: CppCreator,
6+
PythonCreator.code_type: PythonCreator
7+
}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
from leetcode_local_tester.creator.creator import CodeCreator
2+
from leetcode_local_tester.model.problem import Function, Problem
3+
import os
4+
from string import Template
5+
import subprocess
6+
7+
8+
class CppCreator(CodeCreator):
9+
code_type = "cpp"
10+
11+
def format_file(self, file_loc):
12+
subprocess.call(["clang-format", f"-style=file:{self._template_dir}/.clang-format", "-i", file_loc])
13+
14+
"""
15+
// problem can be divided into 4 types:
16+
// function - no predefined type, most of the problems are this type
17+
// function - with predefined type, such as LC174C
18+
// method - no predefined type, such as LC175C
19+
// method - with predefined type, such as LC163B
20+
"""
21+
22+
def parse_code(self, code):
23+
lines = code.split("\n")
24+
# get class name
25+
class_name = ""
26+
for l in lines:
27+
i = l.find("class")
28+
if i != -1:
29+
class_name = l[6:-2]
30+
break
31+
functions = list()
32+
is_func_problem = True
33+
for lo, line in enumerate(lines):
34+
line = line.strip()
35+
if "{" in line and not line.startswith("struct") and not line.startswith("class") and not line.startswith(
36+
"/*") and not line.startswith("//") and not line.startswith("*"):
37+
f = Function()
38+
i = line.find("(")
39+
left = line[:i]
40+
right = line[i + 1:]
41+
left_words = left.split(" ")
42+
f.name = left_words[-1].strip()
43+
f.is_constructor = (f.name == class_name)
44+
if f.is_constructor:
45+
is_func_problem = False
46+
f.location = lo
47+
name_len = len(f.name)
48+
f.output_params = left[:-name_len - 1].strip()
49+
i = right.find(")")
50+
right = right[:i]
51+
right_words = right.split(",")
52+
for w in right_words:
53+
w = w.strip()
54+
f.input_params.append(w)
55+
functions.append(f)
56+
return class_name, is_func_problem, functions
57+
58+
def create_main_code(self, dir_loc, code):
59+
file_location = f"{dir_loc}/solution.h"
60+
d = {
61+
"problem": code,
62+
}
63+
64+
with open(f"{self._template_dir}/solve.h", "r", encoding="utf-8") as f:
65+
src = Template(f.read())
66+
result = src.substitute(d)
67+
68+
with open(file_location, "w", encoding="utf-8") as f:
69+
f.writelines(result)
70+
71+
self.format_file(file_location)
72+
73+
def _create_func_problem_test(self, dir_loc, p: Problem):
74+
f = p.functions[0]
75+
sol = f"{p.class_name} sol = {p.class_name}();"
76+
77+
input_number = len(f.input_params)
78+
build_params_str_list = []
79+
input_names = []
80+
for idx, input_param in enumerate(f.input_params):
81+
if input_param == "":
82+
continue
83+
tmp = input_param.split(" ")
84+
input_type = tmp[0]
85+
input_name = tmp[1]
86+
87+
# remove & symbol
88+
input_type = input_type.replace("&", "")
89+
build_params_str_list.append(f"{input_type} {input_name};")
90+
build_params_str_list.append(f"convert_params(data[i * one_test_number + {idx}], {input_name});")
91+
input_names.append(input_name)
92+
93+
build_params_str_list.append(f"{f.output_params} real_res;")
94+
build_params_str_list.append(f"convert_params(data[i * one_test_number + {input_number}], real_res);")
95+
build_params = "\n".join(build_params_str_list)
96+
run_str = f"{f.output_params} my_res = sol.{f.name}({', '.join(input_names)});"
97+
d = {
98+
"input_number": input_number,
99+
"sol": sol,
100+
"build_params": build_params,
101+
"run": run_str,
102+
}
103+
104+
file_location = f"{dir_loc}/main.cpp"
105+
106+
with open(f"{self._template_dir}/main.cpp", "r", encoding="utf-8") as f:
107+
src = Template(f.read())
108+
result = src.substitute(d)
109+
110+
with open(file_location, "w", encoding="utf-8") as f:
111+
f.writelines(result)
112+
113+
self.format_file(file_location)
114+
115+
def _create_method_problem_test(self, dir_loc, p: Problem):
116+
name_to_function = {}
117+
118+
for f in p.functions:
119+
name_to_function[f.name] = f
120+
121+
def work(func_name):
122+
res = []
123+
f = name_to_function[func_name]
124+
input_params = f.input_params
125+
output_params = f.output_params
126+
127+
input_names = []
128+
for idx, input_param in enumerate(input_params):
129+
if input_param == '':
130+
continue
131+
tmp = input_param.split(" ")
132+
input_type = tmp[0]
133+
input_name = tmp[1]
134+
135+
# remove & symbol
136+
input_type = input_type.replace("&", "")
137+
res.append(f"{input_type} {input_name};")
138+
res.append(f"convert_params(input_param[{idx}], {input_name});")
139+
input_names.append(input_name)
140+
if output_params == "void":
141+
res.append(f"sol.{func_name}({', '.join(input_names)});")
142+
elif output_params == '':
143+
res.append(f"{p.class_name} sol = {p.class_name}({', '.join(input_names)});")
144+
else:
145+
res.append(f"{output_params} real_res;")
146+
res.append(f"convert_params(output_params[j], real_res);")
147+
res.append(f"{output_params} my_res = sol.{func_name}({', '.join(input_names)});")
148+
res.append(
149+
f"bool check_result = compare_result(to_string(i + 1) + \"-\" + to_string(j), my_res, real_res);")
150+
res.append("all_test++;")
151+
res.append("if (!check_result) fail_test++;")
152+
res = "\n".join(res)
153+
return res
154+
155+
constructor = name_to_function[p.class_name]
156+
sol = work(constructor.name)
157+
build_func = []
158+
159+
for idx, f in enumerate(p.functions):
160+
if f.name == p.class_name:
161+
continue
162+
build_func.append(f"if (function_names[j] == \"{f.name}\") " + "{")
163+
build_func.append(f"{work(f.name)}")
164+
build_func.append("}")
165+
166+
build_func = "\n".join(build_func)
167+
168+
d = {
169+
"sol": sol,
170+
"build_func": build_func
171+
}
172+
173+
file_location = f"{dir_loc}/main.cpp"
174+
175+
with open(f"{self._template_dir}/method_main.cpp", "r", encoding="utf-8") as f:
176+
src = Template(f.read())
177+
result = src.substitute(d)
178+
179+
with open(file_location, "w", encoding="utf-8") as f:
180+
f.writelines(result)
181+
182+
self.format_file(file_location)
183+
184+
def create_test_code(self, dir_loc, p: Problem):
185+
if p.is_func_problem:
186+
self._create_func_problem_test(dir_loc, p)
187+
else:
188+
self._create_method_problem_test(dir_loc, p)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
2+
class CodeCreator(object):
3+
4+
code_type = ""
5+
6+
def __init__(self, template_dir):
7+
self._template_dir = template_dir
8+
9+
def parse_code(self, code: str):
10+
"""
11+
According to the code, parse the class name, is_func_problem and functions
12+
:param code: code
13+
:return: class_name, is_func_problem, functions
14+
"""
15+
16+
def create_main_code(self, dir_loc, code):
17+
"""
18+
Generate main code
19+
:param dir_loc: dir location
20+
:param code: code
21+
:return: None
22+
"""
23+
24+
def create_test_code(self, dir_loc, p):
25+
"""
26+
Generate test code
27+
:param dir_loc: dir location
28+
:param p: problem
29+
:return: None
30+
"""

0 commit comments

Comments
 (0)