whyšŸ”—

Time to time I practice on platforms like Leetcode, Codewars, and others. I needed a simple way of organizing the tasks and solutions, a possibility to edit codes in an IDE, running tests. The problem with default approach is that typically you need to "create a project", - so for Java it would be pom.xml and all that stuff; it works while you have just a few tasks, but it all dies when it is a hundred of them.

whatšŸ”—

This is a simple approach that allows quick editing, running tests from CLI, ordering and organizing tasks, using IDE without bloating "projects".

files and foldersšŸ”—

I practice with a few languages, so I have fodler structure per-language.

  1. create a folder where you will keep your coding art - for example, katas
  2. create per-language subfolders. For example, katas/java and katas/ts (for TypeScript)
  3. create folders for problems' complexity: katas/java/easy, katas/java/medium, katas/java/hard. Repeat for each language.

VSCode workspacešŸ”—

Create katas/katas.code-workspace file and put the following content into it:

{
  "folders": [
    { "path": "ts" },
    { "path": "java" },
    { "path": "ocaml" },
    { "path": "lisp" }
  ],
  "settings": {
    // --- Keep the Java extension relaxed (important) ---
    "java.import.maven.enabled": false,
    "java.import.gradle.enabled": false,
    "java.project.importOnFirstTimeStartup": "disabled",
    "java.configuration.updateBuildConfiguration": "disabled",
    // Avoid aggressive indexing of random folders
    "search.exclude": {
      "**/.git": true,
      "**/.idea": true,
      "**/target": true,
      "**/.gradle": true,
      "**/node_modules": true,
      "**/_build": true
    },
    "files.watcherExclude": {
      "**/.git/**": true,
      "**/.idea/**": true,
      "**/node_modules/**": true
    }
  }
}

So, now you can open in VSCode the workspace you just created, it will open the folder and show you the files tree.

configuring TypeScriptšŸ”—

For TypeScript solutions I use Deno - "the open-source JavaScript runtime for the modern web". Go to their website and install it.

configuring Deno for VSCodešŸ”—

I use VSCode. If you use something else, you can skip this section.

Install Deno VSCode extension - that will help

To fix VSCode warnings, create katas/ts/.vscode/settings.json with the following content:

{
  "deno.enable": true,
  "deno.unstable": true
}

To enable Deno only in the TypeScript folder, create katas/.vscode/settings.json with the following content:

{
  "deno.enable": false
}

using the TypeScript approachšŸ”—

Create a new file katas/ts/easy/kata.test.ts:

// code - here you write your solution
export function sum(a: number, b: number) { return a + b; }

// tests - here you put your tests
Deno.test("sum works", () => {
  if (sum(2, 3) !== 5) throw new Error("nope");
});

And now you can run it - open a terminal in the katas folder and run the command: deno test ts/easy/kata.test.ts:

~/katas  deno test ts/easy/kata.test.ts

Check file:///Users/as/katas/ts/easy/kata.test.ts
running 1 test from ./ts/easy/kata.test.ts
sum works ... ok

ok | 1 passed | 0 failed

What happened is that it run the test, the test run the solution function, 1 test passed and 0 tests failed.

configuring JavašŸ”—

For Java solutions I use JBang. Go to their website and install it.

adding common libšŸ”—

With Java, it is usually convenient to have a set of helpers. So, create katas/java/common/CommonBase.java:

import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.engine.discovery.DiscoverySelectors;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import java.io.PrintWriter;

/**
 * This class contains common test-running logic and helper utilities.
 * It will be "included" by individual problems files.
 * * JBang will automatically pull in the //DEPS from this file.
 */
public class CommonBase {

    /**
     * A reusable test runner.
     * @param testClasses The specific test class(es) to run.
     */
    public static void runTests(Class<?>... testClasses) {
        var launcher = LauncherFactory.create();
        var listener = new SummaryGeneratingListener();
        launcher.registerTestExecutionListeners(listener);

        var requestBuilder = LauncherDiscoveryRequestBuilder.request();
        
        // Discover all test classes passed as arguments
        var selectors = new java.util.ArrayList<DiscoverySelectors>();
        for (var testClass : testClasses) {
            requestBuilder.selectors(DiscoverySelectors.selectClass(testClass));
        }

        launcher.execute(requestBuilder.build());

        var summary = listener.getSummary();
        var writer = new PrintWriter(System.out);
        summary.printTo(writer);
        writer.flush(); // Make sure all output is printed

        // Exit with a non-zero code if tests failed
        if (summary.getTestsFailedCount() > 0) {
            System.out.println("\nšŸ”„ Some tests FAILED!");
            System.exit(1);
        } else {
            System.out.println("\nāœ… All tests PASSED!");
        }
    }


    /**
     * A helper class for common LeetCode utilities.
     * Example: public static ListNode buildList(int[] vals) { ... }
     * Example: public static TreeNode buildTree(Integer[] vals) { ... }
     */
    public static class Helpers {
        
        // Example helper:
        public static void printArray(int[] arr) {
            System.out.println(java.util.Arrays.toString(arr));
        }

        // Add your other common helpers here
    }
}

So it helps with running tests and various helpers can be placed here as well.

using the Java approachšŸ”—

Create a test solution katas/java/easy/TwoSum.java

//DEPS org.junit.jupiter:junit-jupiter:5.10.2
//DEPS org.junit.platform:junit-platform-launcher:1.10.2

//SOURCES ../common/CommonBase.java

// We can import the test tools and helpers from the included file
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;

/**
 * This is the main file for the TwoSum problem.
 */
public class TwoSum {
  // --- SOLUTION CLASS ---
    class Solution {
        public int[] twoSum(int[] nums, int target) {
            java.util.Map<Integer, Integer> map = new java.util.HashMap<>();
            for (int i = 0; i < nums.length; i++) {
                int complement = target - nums[i];
                if (map.containsKey(complement)) {
                    return new int[] { map.get(complement), i };
                }
                map.put(nums[i], i);
            }
            return null;
        }
    }
    // --- END OF SOLUTION CLASS


    // --- JUNIT TESTS ---
    static class TwoSumTests {

        @Test
        void testExample1() {
            Solution sol = new TwoSum().new Solution();
            int[] nums = {2, 7, 11, 15};
            int target = 9;
            // You can use your helpers!
            // printArray(sol.twoSum(nums, target)); 
            assertArrayEquals(new int[]{0, 1}, sol.twoSum(nums, target));
        }

        @Test
        void testExample2() {
            Solution sol = new TwoSum().new Solution();
            int[] nums = {3, 2, 4};
            int target = 6;
            assertArrayEquals(new int[]{1, 2}, sol.twoSum(nums, target));
        }
    }
    // --- END OF JUNIT TESTS


    /**
     * The main entry point.
     * All it does is tell the CommonBase to run this file's tests.
     */
    public static void main(String[] args) {
        System.out.println("Running tests for TwoSum...");
        CommonBase.runTests(TwoSumTests.class);
    }
}

Now run command: jbang java/easy/TwoSum.java

This is what you should see if everything works:

Running tests for TwoSum...

Test run finished after 41 ms
[         2 containers found      ]
[         0 containers skipped    ]
[         2 containers started    ]
[         0 containers aborted    ]
[         2 containers successful ]
[         0 containers failed     ]
[         2 tests found           ]
[         0 tests skipped         ]
[         2 tests started         ]
[         0 tests aborted         ]
[         2 tests successful      ]
[         0 tests failed          ]


āœ… All tests PASSED!

conclusionšŸ”—

I have a very simple approach of editing and testing code problems for platforms such as Leetcode and others.