Skip to main content

A C preprocessor and command line utility for leetcode.

Project description

PyPI

icanc

Everything is so clear now.
I can C!
I can fight!

Learning data structures and algorithms should be done in C. Leetcode is a good way to practice and showcase coding skills, but I have never been thrilled about being constrained to a single source file.

icanc is a C preprocessor for bundling includes into a single source file you can submit to online judges. It also includes quality of life commands for generating source files and testing solutions.

See it in action

Utilities

https://github.com/voxelstack/icanc/assets/87827018/70e1ba31-66c9-4126-8a54-a9a946ecf740

Preprocessor

https://github.com/user-attachments/assets/9b905f69-1135-405a-b487-58f4b061912e

udebug integration

https://github.com/user-attachments/assets/06268dd6-3705-4504-9b4d-c36a8c920e58

See an example

My leetcode repository is powered by icanc.

Getting started

Installation

icanc is available as a pip package:

pipx install icanc

Create a new project

icanc creates a leetcode repository for you. Commands must be ran from inside the project root.

# Follow the prompts to create a project.
icanc init

Try it out

Your new project comes with a solution and testcases for beecrowd/1000.

Take a look at problems/beecrowd/1000/solution.c and problems/beecrowd/1000/testcases.toml, and then check if the solution is correct:

# Test the default solution.c using testcases.toml
icanc test beecrowd 1000

The test should pass, which means we are ready to submit:

# Generate the submission for the default solution.c.
icanc submit beecrowd 1000 -c

The -c from the previous command copied the resulting submission to the clipboard, so you can go paste it on beecrowd/1000 and submit.

world.execute(me)

That's the gist of it!

To solve another problem, scaffold a new solution:

# Create a solution and testcases for beecrowd/1001
icanc scaffold beecrowd 1001

Watching for changes

You can run the test command in watch mode. While running, it will watch for changes to the include/ directory and to the current problem's directory, rerunning all tests whenever a file changes.

# Test the default solution.c using testcases.toml
# Rerun on change.
icanc test beecrowd 1000 -w

Multiple solutions

You may want to have multiple solutions for the same problem, either to practice different algorithms, optimize for different parameters, or showcase and compare alternate solutions.

To create a new solution for an existing problem, you can use the -s option:

# Create a new solution for beecrowd/1000 called alt.c
icanc create solution beecrowd 1000 -s alt

# Test your new solution
icanc test beecrowd 1000 -s alt

Multiple testcases

You may want to have multiple testcases for the same problem, either to organize your tests or to copy samples from something like udebug. When you test your solution, all testcases will be used.

To create new testcases for an existing problem, you can use the -t option:

# Create a new testcases file for beecrowd/1000 called alt.
icanc create testcases beecrowd 1000 -t alt

# Test your solution.
icanc test beecrowd 1000

udebug integration

If you have API access to udebug, icanc can download udebug testcases for you.

On icancrc.toml, configure the minimum amount of votes a udebug input must have to be downloaded (defaults to 5):

[udebug]
min_votes = 5 # Inputs that have less than min_votes
              # will not be downloaded.

And remap your judge names to the names used by the udebug API. For example, my leetcode repo uses the name beecrowd, while udebug uses URI:

Special thank you to Vinit Shah for the help with API access.

[udebug.judge_alias]
beecrowd = "URI" # Map the judge names used on your repo
                 # to the judge names used on udebug.

After configuring icanc, authenticate with udebug:

# Store your username and password to the system keyring.
icanc udebug auth

And then you can download testcases directly from udebug:

icanc create testcases beecrowd 1069 -u

Project structure

leet/
├─ binaries/
├─ include/
│  └─ icanc.h
├─ problems/
│  └─ beecrowd/
│     └─ 1000/
│        ├─ solution.c
│        └─ testcases.toml
├─ submissions/
├─ templates/
│  ├─ solve_one.c
│  └─ solve_many.c
├─ icancrc.toml
├─ LICENSE
└─ README.md

leet/

The init command will create a project folder with a name of your choice. All your code lives inside this folder, and this is where you should run commands from.

binaries/

When solutions get compiled for testing, the resulting binaries will be placed on the binaries/ folder. They are organized in subfolders by judge and problem.

icanc tests the compiled binaries automatically so you should not need to interact with this folder.

You should not commit this folder (the generated .gitignore already takes care of that).

include/

This is where your header library will live. When compiling solutions the include/ folder will be on your include path. Subfolders are supported, so you can organize your sources as you like.

To include files from your header library you must use a #pragma icanc include block.

problems/

Your problem solutions live here, along with their testcases. They are organized in subfolders by judge and problem.

icanc generates these files for you, so you should not create files manually here.

submissions/

When solutions get bundled for submitting, the resulting sources will be placed on the submissions/ folder. They are organized in subfolders by judge and problem.

icanc can copy submissions to the clipboard automatically, so you should not need to interact with this folder.

You should not commit this folder (the generated .gitignore already takes care of that).

templates/

When you create a new solution to a problem, it copies the source code from a given template. Those templates live in the templates/ folder. You are free to manage your own templates, but keep in mind that main.c is the default and should not be removed.

icancrc.toml

This file allows you to configure icanc for the current project.

The preprocessor

When you generate a submission, all icanc imports will be resolved recursively and then copied over to your submission file, replacing the include directive. It works just like the C preprocessor, meaning it's equivalent to copying the header file into each source file that needs it.

Of course, all restrictions from normal header files still apply. For example, you cannot include two header files that define a symbol witht he same name. The preprocessor respects the #pragma once directive and will not include a file twice (if the file contains the directive).

With icanc, you can create a header library inside the include/ directory and include headers normally on your leetcode solution! No more messy solutions with all the code dumped into a single file.

#pragma icanc include

Including files from the include/ directory must be done inside a #pragma icanc include block. Includes inside that block will be preprocessed recursively, and the include directives will be removed from the final submission. If you include a file from the include/ directory outside a #pragma icanc include block, it will not be preprocessed and your submission will give you a compilation error.

Headers that are normally available on online judges must be included outside the #pragma icanc include block.

Example usage

// include/icanc.h

#include <stdio.h>

void say()
{
    printf("Hello from icanc.h");
}
// include/greeter.h

/*
 * icanc.h is from our header library,
 * it must be included inside a #pragma icanc include block.
 *
 * You may add multiple include directives inside the same block.
 */
#pragma icanc include
#include <icanc.h>
#pragma icanc end

void greet()
{
    // Use a function from the included header.
    say();
}
// problems/judge/problem/solution.c

#include <stdio.h>

#pragma icanc include
#include <greeter.h>
#pragma icanc end

int main()
{
    greet();
    return 0;
}

Commands

For information on how to use each command, run the command with --help.

ci

Test everything with machine friendly output and exit codes.

create

Create solutions or testcases.

init

Initialize an icanc project.

scaffold

Create a solution and testcase files.

submit

Bundle solution into a single source file for submission.

test

Test a solution against given testcases.

udebug

udebug integration.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

icanc-0.5.2.tar.gz (17.8 kB view hashes)

Uploaded Source

Built Distribution

icanc-0.5.2-py3-none-any.whl (20.8 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page