Skip to content

feat!: 157 daily problem design #158

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

Merged
merged 13 commits into from
Jun 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/daily.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
data
${{ secrets.PROBLEM_FOLDER || 'problems' }}
${{ secrets.PREMIUM_FOLDER || 'premiums' }}
daily-${{ secrets.PROBLEM_FOLDER || 'problems' }}.json

- name: Set up Python environment
uses: actions/setup-python@v2
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/daily_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ jobs:
data
${{ secrets.PROBLEM_FOLDER || 'problems' }}
${{ secrets.PREMIUM_FOLDER || 'premiums' }}
daily-${{ secrets.PROBLEM_FOLDER || 'problems' }}.json

- name: Set up Python environment
uses: actions/setup-python@v2
Expand Down
7 changes: 6 additions & 1 deletion BUILD
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
load("//:solutions.bzl", "generate_cc_tests")

generate_cc_tests()
generate_cc_tests(enabled = False)

load("@plans//:plans.bzl", "PLANS")
load("//:solutions.bzl", "gen_plans")

gen_plans(plans = PLANS)
17 changes: 6 additions & 11 deletions MODULE.bazel
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module(name = "cpp")

# Hedron's Compile Commands Extractor for Bazel
# https://github.com/hedronvision/bazel-compile-commands-extractor
bazel_dep(name = "hedron_compile_commands", dev_dependency = True)
Expand All @@ -12,16 +14,9 @@ git_override(
bazel_dep(name = "googletest", version = "1.17.0")
bazel_dep(name = "nlohmann_json", version = "3.12.0")

new_local_repository = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")

new_local_repository(
name = "problems",
build_file = "//cpp:solution.BUILD",
path = "problems/problems_3445/",
)
load_daily_question = use_extension("//:extensions.bzl", "load_daily_question")
use_repo(load_daily_question, "problems")

new_local_repository(
name = "problem0",
path = "problems/problems_LCR_115/",
build_file = "//cpp:solution.BUILD",
)
daily_plans = use_repo_rule("//:extensions.bzl", "daily_plans")
daily_plans(name = "plans")
12 changes: 7 additions & 5 deletions cpp/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,21 @@

First install bazel environment,

**change path of problem ` path = "problems/problems_2028/",` in [BAZEL MODULE](../MODULE.bazel)**, and try:
For daily:
**change daily in [daily.json](../daily-problems.json)** `Note: the json file is under root with your problem folder, named 'daily-${folder}.json'` and try:
```shell
bazel test --cxxopt=-std=c++23 --cxxopt=-O2 --cxxopt=-fsanitize=address --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=1 --linkopt=-fsanitize=address --test_timeout=3 --test_output=all //cpp:solution_test
```

or if you want to run more than one questions,
**change problem and path in `new_local_repository(name = "problem0", path = "problems/problems_1/"` in [MODULE](../MODULE.bazel)** and maybe add the name ref `@problem0` in [BUILD](tests/BUILD), and try:
or for multiple problems from plans,
**change plans in [daily.json](../daily-problems.json)** `Note: the json file is under root with your problem folder, named 'daily-${folder}.json'` and try:
```shell
bazel test --cxxopt=-std=c++23 --cxxopt=-O2 --cxxopt=-fsanitize=address --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=1 --linkopt=-fsanitize=address --test_timeout=10 --test_output=all //cpp/tests:all
bazel test --cxxopt=-std=c++23 --cxxopt=-O2 --cxxopt=-fsanitize=address --cxxopt=-D_GLIBCXX_USE_CXX11_ABI=1 --linkopt=-fsanitize=address --test_timeout=10 --test_output=all //:all
```

## Environment setup for idea:
First Change the path of the problem in the [solutions.bzl](../solutions.bzl) file

change line `generate_cc_tests(enabled = False)` to `generate_cc_tests(enabled = True)` in [BUILD](../BUILD)

[bazel-compile-commands-extractor](https://github.com/hedronvision/bazel-compile-commands-extractor)
```shell
Expand Down
21 changes: 0 additions & 21 deletions cpp/tests/BUILD

This file was deleted.

4 changes: 4 additions & 0 deletions daily-problems.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"daily": "3445",
"plans": ["1", "problems", "LCR_115", "problems"]
}
50 changes: 50 additions & 0 deletions extensions.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## extensions.bzl
load("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository")

def _format_path(folder, problem):
"""Format the path for a problem."""
return "%s/%s_%s/" % (folder, folder, problem)

def _load_daily_question_impl(ctx):
script = ctx.path(Label("//:get_daily_path.py"))
root = ctx.path(Label("//:MODULE.bazel")).dirname
result = ctx.execute([ctx.which("python3"), script, root])
if result.return_code != 0:
fail("Failed to get daily problem path: %s" % result.stderr)
# result in three lines
s = result.stdout.strip()
splits = s.splitlines()
if len(splits) != 3:
fail("Expected three lines in output, got: %s" % s)
folder = splits[0]
daily_problem = splits[1]
new_local_repository(
name = "problems",
build_file = "//cpp:solution.BUILD",
path = _format_path(folder, daily_problem),
)

load_daily_question = module_extension(
implementation = _load_daily_question_impl,
)

def _impl(rctx):
script = rctx.path(Label("//:get_daily_path.py"))
root = rctx.path(Label("//:MODULE.bazel")).dirname
result = rctx.execute([rctx.which("python3"), script, root])
if result.return_code != 0:
fail("Failed to get daily problem path: %s" % result.stderr)
s = result.stdout.strip()
splits = s.splitlines()
if len(splits) != 3:
fail("Expected three lines in output, got: %s" % s)
plans = splits[2]
rctx.file("BUILD", "exports_files([\"plans.bzl\"])\nvisibility = [\"//visibility:public\"]\n")
rctx.file("plans.bzl", """\
PLANS = %s
""" % ("[" + ",".join(['"%s"' % p for p in plans.split(",")]) + "]"))


daily_plans = repository_rule(
implementation = _impl,
)
22 changes: 22 additions & 0 deletions get_daily_path.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os, json

def parse_env(path):
d = {}
if os.path.exists(path):
for line in open(path):
if '=' in line and not line.strip().startswith('#'):
k, v = line.strip().split('=', 1)
d[k.strip()] = v.strip().strip('"').strip("'")
return d

root = os.path.dirname(__file__)
env = parse_env(os.path.join(root, ".env"))
folder = env.get("PROBLEM_FOLDER", "problems")
json_path = os.path.join(root, f"daily-{folder}.json")
with open(json_path) as f:
data_json = json.load(f)
daily = data_json.get("daily", "1")
plans = data_json.get("plans", [])
print(folder)
print(daily)
print(",".join(plans))
105 changes: 103 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>LeetCode.qubhjava</groupId>
<artifactId>qubhjava</artifactId>
<version>1.0.0</version>
<version>2.0.0</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>21</maven.compiler.release>
Expand Down Expand Up @@ -68,6 +68,107 @@
</dependencies>
<build>
<plugins>

<!-- Groovy脚本执行器 -->
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>4.2.0</version>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>3.0.25</version>
<type>pom</type>
</dependency>
</dependencies>
<executions>
<execution>
<id>parse-config</id>
<phase>initialize</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<scripts>
<script><![CDATA[
import groovy.json.JsonSlurper
import java.nio.file.*

// 1. 读取.env文件
def envFile = new File("${project.basedir}/.env")
def baseDir = "problems"
if (envFile.exists()) {
envFile.eachLine { line ->
if (line.startsWith("PROBLEMS_FOLDER=")) {
baseDir = line.split("=")[1].trim()
}
}
}

// 2. 读取config.json
def configFile = new File("${project.basedir}/daily-${baseDir}.json")
if (!configFile.exists()) {
throw new RuntimeException("Config file not found: ${configFile.absolutePath}")
}
def config = new JsonSlurper().parse(configFile)

// 3. 构建源目录列表
def sources = [] as Set
sources.add(new File(project.basedir, "${baseDir}/${baseDir}_${config.daily}/").absolutePath)
for (int i = 0; i < config.plans.size(); i += 2) {
def problem = config.plans[i]
def folder = config.plans[i + 1]
sources.add(new File(project.basedir, "${folder}/${folder}_${problem}/").absolutePath)
}

println "==> 添加动态源目录:"
sources.each { println " - $it" }

// 4. 设置Maven属性
sources.eachWithIndex { source, index ->
project.properties["dynamic.source${index + 1}"] = source
}
// 5. 补全10个
(sources.size()..9).each { index ->
project.properties["dynamic.source${index + 1}"] = "qubhjava"
}
]]></script>
</scripts>
</configuration>
</execution>
</executions>
</plugin>
<!-- 添加动态源目录插件 -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.6.1</version>
<executions>
<execution>
<id>add-problem-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${dynamic.source1}</source>
<source>${dynamic.source2}</source>
<source>${dynamic.source3}</source>
<source>${dynamic.source4}</source>
<source>${dynamic.source5}</source>
<source>${dynamic.source6}</source>
<source>${dynamic.source7}</source>
<source>${dynamic.source8}</source>
<source>${dynamic.source9}</source>
<source>${dynamic.source10}</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
Expand All @@ -92,7 +193,7 @@
</configuration>
</plugin>
</plugins>
<sourceDirectory>.</sourceDirectory>
<sourceDirectory>qubhjava</sourceDirectory>
<testSourceDirectory>qubhjava/test</testSourceDirectory>
</build>
</project>
4 changes: 2 additions & 2 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
Since the requirements are installed as root project needed, so not necessary to install again.

Simply,
**change QUESTION id in [test.py](test.py)**, and try:
**change daily in [daily.json](../daily-problems.json)** `Note: the json file is under root with your problem folder, named 'daily-${folder}.json'` and try:
```shell
python3 python/test.py
```

or if you want to run more than one questions,
**change QUESTIONS in [tests.py](tests.py)**, and try:
**change plans in [daily.json](../daily-problems.json)** `Note: the json file is under root with your problem folder, named 'daily-${folder}.json'` and try:
```shell
python3 python/tests.py
```
Expand Down
61 changes: 3 additions & 58 deletions python/lc_libs/cpp_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,68 +13,13 @@ def __init__(self) -> None:
super().__init__()
self.solution_file = "Solution.cpp"
self.main_folder = ""
self.test_file = "MODULE.bazel"
self.tests_files = ["MODULE.bazel", "cpp/tests/BUILD"]
self.lang_env_commands = [["bazel", "version"]]
self.test_commands = [
["bazel", "test", "--cxxopt=-std=c++20", "//cpp:solution_test"]
["bazel", "test", "--cxxopt=-std=c++23", "--cxxopt=-O2", "--cxxopt=-fsanitize=address",
"--cxxopt=-D_GLIBCXX_USE_CXX11_ABI=1", "--linkopt=-fsanitize=address",
"--test_timeout=10","//cpp:solution_test"]
]

def change_test(self, root_path, problem_folder: str, question_id: str):
test_file_path = os.path.join(root_path, self.test_file)
with open(test_file_path, "r", encoding="utf-8") as f:
content = f.read()
with open(test_file_path, "w", encoding="utf-8") as f:
is_problem = False
lines = content.split("\n")
for line_idx, line in enumerate(lines):
if 'name = "problems",' in line:
is_problem = True
elif is_problem and 'path = "' in line:
f.write(
' path = "{}/{}_{}/",\n'.format(
problem_folder, problem_folder, question_id
)
)
is_problem = False
continue
if line_idx < len(lines) - 1 or line:
f.write(line + "\n")

def change_tests(self, root_path, problem_ids_folders: list):
test_file_path0 = os.path.join(root_path, self.tests_files[0])
with open(test_file_path0, "r", encoding="utf-8") as f:
content = f.read()
with open(test_file_path0, "w", encoding="utf-8") as f:
lines = content.split("\n")
ans = []
idx = 0
while idx < len(lines):
if "new_local_repository(" in lines[idx]:
if 'name = "problems",' in lines[idx + 1]:
ans.extend(lines[idx: idx + 5])
idx += 5
while idx < len(lines) and lines[idx].strip() == "":
idx += 1
continue
ans.append(lines[idx])
idx += 1
ans.append("")
for i, (problem_id, problem_folder) in enumerate(problem_ids_folders):
ans.append("new_local_repository(")
ans.append(f' name = "problem{i}",')
ans.append(
f' path = "{problem_folder}/{problem_folder}_{problem_id}/",'
)
ans.append(' build_file = "//cpp:solution.BUILD",')
ans.append(")")
ans.append("")
f.write("\n".join(ans))
test_file_path1 = os.path.join(root_path, self.tests_files[1])
with open(test_file_path1, "w", encoding="utf-8") as f:
for i, (problem_id, _) in enumerate(problem_ids_folders):
f.write(TESTCASE_TEMPLATE_CPP.format(problem_id, i, i, i) + "\n")

def write_solution(
self,
code_default: str,
Expand Down
Loading