1
1
mirror of https://github.com/theoludwig/programming-challenges.git synced 2025-05-18 12:02:53 +02:00

Compare commits

..

9 Commits

70 changed files with 5194 additions and 4415 deletions

View File

@ -1 +0,0 @@
{ "extends": ["@commitlint/config-conventional"] }

View File

@ -10,7 +10,7 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{py,cs,rs,java}]
[*.{py,cs,rs,java,md}]
indent_size = 4
[*.txt]

View File

@ -1,18 +0,0 @@
{
"extends": ["conventions"],
"plugins": ["import", "unicorn"],
"parserOptions": {
"project": "./tsconfig.json"
},
"rules": {
"import/extensions": ["error", "always"],
"unicorn/prevent-abbreviations": "error",
"unicorn/prefer-node-protocol": "error"
},
"overrides": [
{
"files": ["*.ts", "*.tsx"],
"parser": "@typescript-eslint/parser"
}
]
}

View File

@ -6,8 +6,8 @@ labels: "bug"
---
<!--
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please make sure your issue has not already been fixed.
Please provide a clear and concise description of what the bug is. Include
screenshots if needed. Please make sure your issue has not already been fixed.
-->
## Steps To Reproduce

View File

@ -11,7 +11,7 @@ jobs:
runs-on: "ubuntu-latest"
timeout-minutes: 30
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.2.2"
with:
fetch-depth: 0
@ -21,7 +21,7 @@ jobs:
SKIP_LOGIN: true
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.1"
uses: "actions/setup-node@v4.4.0"
with:
node-version: "lts/*"
cache: "npm"

View File

@ -10,10 +10,10 @@ jobs:
lint:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.2.2"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.1"
uses: "actions/setup-node@v4.4.0"
with:
node-version: "lts/*"
cache: "npm"
@ -21,18 +21,18 @@ jobs:
- name: "Install dependencies"
run: "npm clean-install"
- run: 'npm run lint:commit -- --to "${{ github.sha }}"'
- run: "npm run lint:editorconfig"
- run: "npm run lint:markdown"
- run: "npm run lint:eslint"
- run: "npm run lint:prettier"
build:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.2.2"
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.1"
uses: "actions/setup-node@v4.4.0"
with:
node-version: "lts/*"
cache: "npm"
@ -43,12 +43,12 @@ jobs:
- name: "Build"
run: "npm run build"
- run: "npm run build:typescript"
- run: "npm run lint:typescript"
test:
runs-on: "ubuntu-latest"
steps:
- uses: "actions/checkout@v4.1.1"
- uses: "actions/checkout@v4.2.2"
- name: "Setup Docker"
uses: "actions-hub/docker/cli@master"
@ -56,7 +56,7 @@ jobs:
SKIP_LOGIN: true
- name: "Setup Node.js"
uses: "actions/setup-node@v4.0.1"
uses: "actions/setup-node@v4.4.0"
with:
node-version: "lts/*"
cache: "npm"

3
.gitignore vendored
View File

@ -28,3 +28,6 @@ coverage
.DS_Store
temp
tmp
# typescript
*.tsbuildinfo

View File

@ -1,12 +0,0 @@
{
"config": {
"extends": "markdownlint/style/prettier",
"default": true,
"relative-links": true,
"no-duplicate-heading": false,
"no-inline-html": false,
},
"globs": ["**/*.md"],
"ignores": ["**/node_modules"],
"customRules": ["markdownlint-rule-relative-links"],
}

19
.markdownlint-cli2.mjs Normal file
View File

@ -0,0 +1,19 @@
import relativeLinksRule, { markdownIt } from "markdownlint-rule-relative-links"
const config = {
config: {
extends: "markdownlint/style/prettier",
default: true,
"relative-links": true,
"no-duplicate-heading": false,
"no-inline-html": false,
},
globs: ["**/*.md"],
ignores: ["**/node_modules"],
customRules: [relativeLinksRule],
markdownItFactory: () => {
return markdownIt
},
}
export default config

1
.npmrc
View File

@ -1 +0,0 @@
save-exact=true

3
.prettierrc.json Normal file
View File

@ -0,0 +1,3 @@
{
"semi": false
}

View File

@ -20,21 +20,16 @@ community include:
- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
- Focusing on what is best not just for us as individuals, but for the
overall community
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community
Examples of unacceptable behavior include:
- The use of sexualized language or imagery, and sexual attention or
advances of any kind
- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or email
address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
- Publishing others' private information, such as a physical or email address, without their explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Enforcement Responsibilities

View File

@ -44,7 +44,7 @@ You can add support for a new language, so you can solve the challenges with eve
- Create a new template inside `templates/solution` folder with the file extension of the new programming language, with the basic boilerplate to read from `stdin` and print to `stdout` the result
- Every programming language should have at least one working solution for `challenges/hello-world`.
To generate the solution for the `hello-world` challenge with your new language, run the command `programming-challenges generate solution --challenge="hello-world" --github-user="<your-github-user>" --language="<your-new-language>" --solution="function"`
To generate the solution for the `hello-world` challenge with your new language, run the command `programming-challenges generate solution --challenge="hello-world" --github-user="<your-github-user>" --language="<your-new-language>" --solution="function"`
Before submitting the new programming language, make sure it passes all the tests by running `programming-challenges run test --affected`

View File

@ -1,21 +1,21 @@
<h1 align="center">programming-challenges</h1>
<p align="center">
<strong>Programming exercises and challenges to improve your algorithmic logic.</strong>
<strong>Programming exercises and challenges to improve your algorithmic logic.</strong>
</p>
<p align="center">
<a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="CONTRIBUTING" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a>
<a href="./CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
<br />
<a href="https://github.com/theoludwig/programming-challenges/actions/workflows/cli.yml"><img src="https://github.com/theoludwig/programming-challenges/actions/workflows/cli.yml/badge.svg?branch=master" alt="CLI" /></a>
<a href="https://github.com/theoludwig/programming-challenges/actions/workflows/challenges.yml"><img src="https://github.com/theoludwig/programming-challenges/actions/workflows/challenges.yml/badge.svg?branch=master" alt="Challenges" /></a>
<a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a>
<br/>
<a href="https://gitpod.io/#https://github.com/theoludwig/programming-challenges"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod" alt="Gitpod ready-to-code"/></a>
<br/> <br/>
<img src="./logo.png" width="120" alt="programming-challenges Logo" />
<a href="./CONTRIBUTING.md"><img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="CONTRIBUTING" /></a>
<a href="./LICENSE"><img src="https://img.shields.io/badge/licence-MIT-blue.svg" alt="Licence MIT"/></a>
<a href="./CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>
<br />
<a href="https://github.com/theoludwig/programming-challenges/actions/workflows/cli.yml"><img src="https://github.com/theoludwig/programming-challenges/actions/workflows/cli.yml/badge.svg?branch=master" alt="CLI" /></a>
<a href="https://github.com/theoludwig/programming-challenges/actions/workflows/challenges.yml"><img src="https://github.com/theoludwig/programming-challenges/actions/workflows/challenges.yml/badge.svg?branch=master" alt="Challenges" /></a>
<a href="https://conventionalcommits.org"><img src="https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg" alt="Conventional Commits" /></a>
<br/>
<a href="https://gitpod.io/#https://github.com/theoludwig/programming-challenges"><img src="https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod" alt="Gitpod ready-to-code"/></a>
<br/> <br/>
<img src="./logo.png" width="120" alt="programming-challenges Logo" />
</p>
## 📜 About
@ -48,7 +48,7 @@ Gitpod will automatically setup an environment for you.
#### Prerequisites
- [Node.js](https://nodejs.org/) >= 20.0.0
- [Node.js](https://nodejs.org/) >= 20.11.0
- [npm](https://npmjs.com/) >= 10.0.0
- [Docker](https://www.docker.com/)
@ -80,17 +80,20 @@ programming-challenges --help
# Generate a new challenge
programming-challenges generate challenge --github-user="YourGitHubName" --challenge="hello-world"
# Search for a challenge not yet solved in a specific programming language
programming-challenges search --language="rust"
# Generate a new solution
programming-challenges generate solution --github-user="YourGitHubName" --challenge="hello-world" --solution="function" --language="python"
# Test a solution
programming-challenges run test --challenge="hello-world" --solution="function" --language="python"
# Run a solution with specific `input.txt` file
programming-challenges run solution --challenge="hello-world" --solution="function" --language="python" --input-path="./challenges/hello-world/test/1/input.txt" --output
# Search for a challenge not yet solved in a specific programming language
programming-challenges search --language="rust"
# Test a solution
programming-challenges run test --challenge="hello-world" --solution="function" --language="python"
# Test all the solutions in all the challenges
programming-challenges run test --all
```
## 💡 Contributing

View File

@ -0,0 +1,25 @@
# a-phone-code
Created by [@theoludwig](https://github.com/theoludwig) on 13 May 2025.
## Instructions
Polycarpus has n friends in Tarasov city. Polycarpus knows phone numbers of all his friends: they are strings s1, s2, ..., sn. All these strings consist only of digits and have the same length.
Once Polycarpus needed to figure out Tarasov city phone code. He assumed that the phone code of the city is the longest common prefix of all phone numbers of his friends. In other words, it is the longest string c which is a prefix (the beginning) of each si for all i. Help Polycarpus determine the length of the city phone code.
### Input
The first line of the input contains an integer n — the number of Polycarpus's friends. The following n lines contain strings — the phone numbers of Polycarpus's friends. It is guaranteed that all strings consist only of digits and have the same length from 1 to 20, inclusive. It is also guaranteed that all strings are different.
### Output
Print the number of digits in the city phone code.
## Source
[Codeforces - A. Phone Code](https://codeforces.com/problemset/problem/172/A)
## Examples
See the `test` folder for examples of input/output.

View File

@ -0,0 +1,3 @@
# a-phone-code/c/function
Created by [@theoludwig](https://github.com/theoludwig) on 13 May 2025.

View File

@ -0,0 +1,7 @@
#include "character.h"
void character_append(char* string, char character) {
size_t length = strlen(string);
string[length] = character;
string[length + 1] = '\0';
}

View File

@ -0,0 +1,16 @@
#ifndef __CHARACTER__
#define __CHARACTER__
#include <stdlib.h>
#include <string.h>
/**
* @brief Append a character to a string, assuming string points to an array
* with enough space.
*
* @param string
* @param character
*/
void character_append(char* string, char character);
#endif

View File

@ -0,0 +1,14 @@
#include "input.h"
char* input() {
char character;
size_t length = 1;
char* string = malloc(length * sizeof(char));
*string = '\0';
while ((character = getchar()) != '\n' && character != EOF) {
length++;
string = realloc(string, length * sizeof(char));
character_append(string, character);
}
return string;
}

View File

@ -0,0 +1,16 @@
#ifndef __INPUT__
#define __INPUT__
#include <stdio.h>
#include <stdlib.h>
#include "character.h"
/**
* @brief Read a line from stdin.
*
* @return char*
*/
char* input();
#endif

View File

@ -0,0 +1,40 @@
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "input.h"
int main() {
size_t count = (size_t)atoi(input());
size_t prefix_length = 0;
char* last = NULL;
for (size_t i = 1; i < count; i++) {
if (last == NULL) {
last = input();
}
char* current = input();
size_t prefix_current_length = 0;
for (size_t j = 0; j < strlen(current); j++) {
if (current[j] == last[j]) {
prefix_current_length += 1;
} else {
break;
}
}
if (prefix_length > prefix_current_length) {
prefix_length = prefix_current_length;
} else if (prefix_length == 0) {
prefix_length = prefix_current_length;
}
last = current;
}
printf("%ld\n", prefix_length);
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,5 @@
4
00209
00219
00999
00909

View File

@ -0,0 +1 @@
2

View File

@ -0,0 +1,3 @@
2
1
2

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1,4 @@
3
77012345678999999999
77012345678901234567
77012345678998765432

View File

@ -0,0 +1 @@
12

View File

@ -16,9 +16,7 @@ make?
### Input
- **Line 1:** Single integer `N` for the number of ingredients.
- **`N` next lines:** One for each ingredient. Each of these lines contains two positive integers:
the first one is the required quantity of this ingredient per cake, the second one is the quantity of
this ingredient you have in your kitchen.
- **`N` next lines:** One for each ingredient. Each of these lines contains two positive integers: the first one is the required quantity of this ingredient per cake, the second one is the quantity of this ingredient you have in your kitchen.
### Output
@ -32,7 +30,7 @@ available ingredients.
## Source
[SWERC 20202021 - Problem E: Cake](https://swerc.eu/2020/problems/)
[SWERC 2020-2021 - Problem E: Cake](https://swerc.eu/2020/problems/)
## Examples

View File

@ -21,7 +21,8 @@ This data is comprised of lines, each of which represents a defibrillator. Each
- Contact Phone number
- Longitude (degrees)
- Latitude (degrees)
These fields are separated by a semicolon (`;`).
These fields are separated by a semicolon (`;`).
**Beware:** the decimal numbers use the comma (,) as decimal separator. Remember to turn the comma (,) into dot (.) if necessary in order to use the data in your program.

View File

@ -14,37 +14,3 @@ Display a pyramid of stars (`*`) whose height is given and in the right order (`
## Examples
See the `test` folder for examples of input/output.
### Example 1
#### Input
```txt
normal
3
```
#### Output
```txt
*
***
*****
```
### Example 2
#### Input
```txt
reverse
3
```
#### Output
```txt
*****
***
*
```

View File

@ -22,9 +22,9 @@ Here are the rules for building a Roman numeral:
- Subtractions of values are limited to 2 letters only. For example we **cannot** write `8` while doing `IIX`. We must use the addition of letters like this `VIII`.
- Therefore, the first ten numbers are written as `I`, `II`, `III`, `IV`, `V`, `VI`, `VII`, `VIII`, `IX`, `X` . Larger numbers follow the same pattern.
- You can associate as many symbols as you want to write larger numbers, for example:
- `36` is written as `XXXVI`
- `42` is written as `XLII`
- `2448` is written as `MMCDXLVIII`.
- `36` is written as `XXXVI`
- `42` is written as `XLII`
- `2448` is written as `MMCDXLVIII`.
| Symbol | I | V | X | L | C | D | M |
| ------ | --- | --- | --- | --- | --- | --- | ---- |

View File

@ -0,0 +1,49 @@
# valid-parentheses
Created by [@theoludwig](https://github.com/theoludwig) on 18 November 2024.
## Instructions
Given a string containing just the characters `'('`, `')'`, `'{'`, `'}'`, `'['` and `']'`, determine if the input string is valid.
An input string is valid if:
- Open brackets must be closed by the same type of brackets.
- Open brackets must be closed in the correct order.
- Every close bracket has a corresponding open bracket of the same type.
## Source
[LeetCode - Valid Parentheses](https://leetcode.com/problems/valid-parentheses)
## Examples
See the `test` folder for examples of input/output.
### Example 1
#### Input
```txt
()
```
#### Output
```txt
true
```
### Example 2
#### Input
```txt
(]
```
#### Output
```txt
false
```

View File

@ -0,0 +1,3 @@
# valid-parentheses/javascript/function
Created by [@theoludwig](https://github.com/theoludwig) on 18 November 2024.

View File

@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@ -0,0 +1,42 @@
import readline from "node:readline"
const input = []
const readlineInterface = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
readlineInterface.on("line", (value) => {
input.push(value)
})
readlineInterface.on("close", solution)
function solution() {
console.log(isValidParentheses(input[0]))
}
const mapCloseOpen = {
")": "(",
"]": "[",
"}": "{",
}
const closeCharacters = Object.keys(mapCloseOpen)
const openCharacters = Object.values(mapCloseOpen)
/**
* @param {string} string
* @return {boolean}
*/
const isValidParentheses = (string) => {
const stack = []
for (let index = 0; index < string.length; index++) {
if (openCharacters.includes(string[index])) {
stack.push(string[index])
} else if (closeCharacters.includes(string[index])) {
const last = stack.pop()
if (last !== mapCloseOpen[string[index]]) {
return false
}
}
}
return stack.length <= 0
}

View File

@ -0,0 +1 @@
()

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
()[]{}

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
(]

View File

@ -0,0 +1 @@
false

View File

@ -0,0 +1 @@
([])

View File

@ -0,0 +1 @@
true

View File

@ -0,0 +1 @@
(abc[def])

View File

@ -0,0 +1 @@
true

View File

@ -30,7 +30,7 @@ await test("programming-challenges run solution", async (t) => {
sinon.restore()
})
await t.test("succeeds", async () => {
await t.test("succeeds", { skip: true }, async () => {
sinon.stub(console, "log").value(() => {})
const consoleLogSpy = sinon.spy(console, "log")
const stream = new PassThrough()

View File

@ -21,7 +21,7 @@ await test("programming-challenges run test", async (t) => {
sinon.restore()
})
await t.test("succeeds", async () => {
await t.test("succeeds", { skip: true }, async () => {
sinon.stub(console, "log").value(() => {})
const consoleLogSpy = sinon.spy(console, "log")
const stream = new PassThrough()

View File

@ -19,7 +19,7 @@ export class Challenge implements ChallengeOptions {
public name: string
public path: string
constructor(options: ChallengeOptions) {
public constructor(options: ChallengeOptions) {
const { name } = options
this.name = name
this.path = fileURLToPath(new URL(`./${name}`, Challenge.BASE_URL))

View File

@ -20,7 +20,7 @@ export interface GitAffectedOptions {
export class GitAffected implements GitAffectedOptions {
public base?: string
constructor(options: GitAffectedOptions = {}) {
public constructor(options: GitAffectedOptions = {}) {
this.base = options.base
}

View File

@ -38,7 +38,7 @@ export class Solution implements SolutionOptions {
public path: string
public temporaryFolder: TemporaryFolder
constructor(options: SolutionOptions) {
public constructor(options: SolutionOptions) {
const { programmingLanguageName, challenge, name } = options
this.programmingLanguageName = programmingLanguageName
this.challenge = challenge
@ -83,7 +83,7 @@ export class Solution implements SolutionOptions {
loader.succeed(chalk.bold.green("Success!"))
SolutionTestsResult.printBenchmark(elapsedTimeMilliseconds)
if (output) {
console.log(`${chalk.bold("Output:")}`)
console.log(chalk.bold("Output:"))
console.log(stdout)
}
} catch (error: any) {
@ -92,7 +92,9 @@ export class Solution implements SolutionOptions {
}
}
static async generate(options: GenerateSolutionOptions): Promise<Solution> {
public static async generate(
options: GenerateSolutionOptions,
): Promise<Solution> {
const { name, challengeName, programmingLanguageName, githubUser } = options
const challenge = new Challenge({ name: challengeName })
if (!(await isExistingPath(challenge.path))) {
@ -116,7 +118,7 @@ export class Solution implements SolutionOptions {
return solution
}
static async get(options: GetSolutionOptions): Promise<Solution> {
public static async get(options: GetSolutionOptions): Promise<Solution> {
const { name, challengeName, programmingLanguageName } = options
const challenge = new Challenge({
name: challengeName,
@ -132,7 +134,9 @@ export class Solution implements SolutionOptions {
return solution
}
static async getManyByChallenge(challenge: Challenge): Promise<Solution[]> {
public static async getManyByChallenge(
challenge: Challenge,
): Promise<Solution[]> {
const solutionsPath = path.join(challenge.path, "solutions")
const languagesSolution = (await fs.promises.readdir(solutionsPath)).filter(
(name) => {
@ -151,7 +155,7 @@ export class Solution implements SolutionOptions {
return await Solution.getManyByPaths(paths)
}
static async getManyByProgrammingLanguages(
public static async getManyByProgrammingLanguages(
programmingLanguages?: string[],
): Promise<Solution[]> {
const languages =
@ -185,7 +189,7 @@ export class Solution implements SolutionOptions {
* @param paths relative to `challenges` (e.g: `challenges/hello-world/solutions/c/function`)
* @returns
*/
static async getManyByPaths(paths: string[]): Promise<Solution[]> {
public static async getManyByPaths(paths: string[]): Promise<Solution[]> {
const solutions: string[] = []
for (const path of paths) {
if (await isExistingPath(path)) {

View File

@ -25,7 +25,7 @@ export class SolutionTestsResult implements SolutionTestsResultOptions {
"Success:",
)} Tests passed! 🎉`
constructor(options: SolutionTestsResultOptions) {
public constructor(options: SolutionTestsResultOptions) {
this.tests = options.tests.sort((a, b) => {
return a.testNumber - b.testNumber
})

View File

@ -2,13 +2,11 @@ import path from "node:path"
import { fileURLToPath } from "node:url"
import fs from "node:fs"
import replaceInFileDefault from "replace-in-file"
import { replaceInFile } from "replace-in-file"
import date from "date-and-time"
import { copyDirectory } from "../utils/copyDirectory.js"
const { replaceInFile } = replaceInFileDefault
const TEMPLATE_PATH = fileURLToPath(new URL("../../templates", import.meta.url))
const TEMPLATE_DOCKER_PATH = path.join(TEMPLATE_PATH, "docker")
const TEMPLATE_CHALLENGE_PATH = path.join(TEMPLATE_PATH, "challenge")

View File

@ -35,7 +35,7 @@ export class Test implements TestOptions {
public output: string
public stdout: string
constructor(options: TestOptions) {
public constructor(options: TestOptions) {
this.testNumber = options.testNumber
this.path = options.path
this.isSuccess = options.isSuccess
@ -44,7 +44,7 @@ export class Test implements TestOptions {
this.stdout = options.stdout
}
static async runAll(solution: Solution): Promise<SolutionTestsResult> {
public static async runAll(solution: Solution): Promise<SolutionTestsResult> {
const testsPath = path.join(solution.challenge.path, "test")
const testsFolders = await fs.promises.readdir(testsPath)
const testsNumbers = testsFolders.map((test) => {
@ -62,7 +62,7 @@ export class Test implements TestOptions {
return new SolutionTestsResult({ solution, tests, elapsedTimeMilliseconds })
}
static async getInputOutput(testPath: string): Promise<InputOutput> {
public static async getInputOutput(testPath: string): Promise<InputOutput> {
const inputPath = path.join(testPath, "input.txt")
const outputPath = path.join(testPath, "output.txt")
const input = await fs.promises.readFile(inputPath, { encoding: "utf-8" })
@ -72,7 +72,9 @@ export class Test implements TestOptions {
return { input, output }
}
static async runManyWithSolutions(solutions: Solution[]): Promise<number> {
public static async runManyWithSolutions(
solutions: Solution[],
): Promise<number> {
const solutionTestsResultsPromises: Array<Promise<SolutionTestsResult>> = []
let isSolutionSuccess = true
for (const solution of solutions) {
@ -85,7 +87,9 @@ export class Test implements TestOptions {
isSolutionSuccess = false
}
})
.catch(() => {})
.catch(() => {
isSolutionSuccess = false
})
}
await Promise.all(solutionTestsResultsPromises)
await TemporaryFolder.cleanAll()
@ -96,7 +100,7 @@ export class Test implements TestOptions {
return 1
}
static async run(options: TestRunOptions): Promise<Test> {
public static async run(options: TestRunOptions): Promise<Test> {
const { input, output } = await Test.getInputOutput(options.path)
try {
const { stdout } = await docker.run(

19
eslint.config.js Normal file
View File

@ -0,0 +1,19 @@
import typescriptESLint from "typescript-eslint"
import configConventions from "eslint-config-conventions"
export default typescriptESLint.config(
...configConventions,
{
ignores: ["challenges/**", "templates/**"],
},
{
files: ["**/*.ts", "**/*.tsx"],
languageOptions: {
parser: typescriptESLint.parser,
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
)

8977
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
"url": "https://github.com/theoludwig/programming-challenges"
},
"engines": {
"node": ">=20.0.0",
"node": ">=20.11.0",
"npm": ">=10.0.0"
},
"main": "build/index.js",
@ -19,51 +19,49 @@
"start": "node --enable-source-maps build/index.js",
"build": "swc ./cli --out-dir ./build --strip-leading-paths --delete-dir-on-start",
"build:dev": "swc ./cli --out-dir ./build --strip-leading-paths --delete-dir-on-start --watch",
"build:typescript": "tsc",
"lint:commit": "commitlint",
"lint:typescript": "tsc --noEmit",
"lint:editorconfig": "editorconfig-checker",
"lint:markdown": "markdownlint-cli2",
"lint:eslint": "eslint ./cli --max-warnings 0 --report-unused-disable-directives --ignore-path .gitignore",
"test": "cross-env NODE_ENV=test node --enable-source-maps --test build/"
"lint:eslint": "eslint ./cli --max-warnings 0",
"lint:prettier": "prettier . --check",
"test": "cross-env NODE_ENV=test node --enable-source-maps --test \"build/**/*.test.js\""
},
"dependencies": {
"chalk": "5.3.0",
"chalk": "5.4.1",
"clipanion": "3.2.1",
"date-and-time": "3.1.1",
"execa": "8.0.1",
"log-symbols": "6.0.0",
"ora": "8.0.1",
"replace-in-file": "7.1.0",
"table": "6.8.1",
"date-and-time": "3.6.0",
"execa": "9.5.3",
"log-symbols": "7.0.0",
"ora": "8.2.0",
"replace-in-file": "8.3.0",
"table": "6.9.0",
"typanion": "3.14.0",
"validate-npm-package-name": "5.0.0"
"validate-npm-package-name": "6.0.0"
},
"devDependencies": {
"@commitlint/cli": "18.6.0",
"@commitlint/config-conventional": "18.6.0",
"@swc/cli": "0.3.5",
"@swc/core": "1.3.107",
"@tsconfig/strictest": "2.0.2",
"@swc/cli": "0.7.7",
"@swc/core": "1.11.24",
"@types/mock-fs": "4.13.4",
"@types/ms": "0.7.34",
"@types/node": "20.11.10",
"@types/sinon": "17.0.3",
"@types/ms": "2.1.0",
"@types/node": "22.15.17",
"@types/sinon": "17.0.4",
"@types/validate-npm-package-name": "4.0.2",
"@typescript-eslint/eslint-plugin": "6.20.0",
"@typescript-eslint/parser": "6.20.0",
"cross-env": "7.0.3",
"editorconfig-checker": "5.1.2",
"eslint": "8.56.0",
"eslint-config-conventions": "13.1.0",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-promise": "6.1.1",
"eslint-plugin-unicorn": "50.0.1",
"get-stream": "8.0.1",
"markdownlint-cli2": "0.12.1",
"markdownlint-rule-relative-links": "2.2.0",
"mock-fs": "5.2.0",
"editorconfig-checker": "6.0.1",
"eslint": "9.26.0",
"eslint-config-conventions": "19.2.0",
"eslint-plugin-import-x": "4.11.1",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-unicorn": "59.0.1",
"get-stream": "9.0.1",
"globals": "16.1.0",
"markdownlint-cli2": "0.18.0",
"markdownlint-rule-relative-links": "4.1.0",
"mock-fs": "5.5.0",
"ms": "2.1.3",
"sinon": "17.0.1",
"typescript": "5.3.3"
"prettier": "3.5.3",
"sinon": "20.0.0",
"typescript": "5.8.3",
"typescript-eslint": "8.32.1"
}
}

View File

@ -1,4 +1,4 @@
FROM gcc:12.3.0-bookworm AS builder
FROM gcc:12.4.0-bookworm AS builder
WORKDIR /usr/src/application
COPY ./ ./
RUN gcc ./*.c* -o solution -Wall -Wextra -Wfloat-equal -Wundef -Werror -std=c17 -pedantic -pedantic-errors -O3

View File

@ -1,4 +1,4 @@
FROM gcc:12.3.0-bookworm AS builder
FROM gcc:12.4.0-bookworm AS builder
WORKDIR /usr/src/application
COPY ./ ./
RUN g++ ./*.cpp* -o solution -Wall -Wextra -Wfloat-equal -Wundef -Werror -std=c++17 -pedantic -pedantic-errors -O3

View File

@ -1,4 +1,4 @@
FROM mono:6.12.0
FROM mono:6.12.0.182
WORKDIR /usr/src/application
COPY ./ ./
RUN mcs ./Solution.cs -out:Solution.exe

View File

@ -1,4 +1,4 @@
FROM dart:3.2.5 AS builder
FROM dart:3.7.3 AS builder
WORKDIR /usr/src/application
COPY ./ ./
RUN dart compile exe solution.dart -o solution

View File

@ -1,10 +1,10 @@
FROM openjdk:17 AS builder
FROM openjdk:21 AS builder
WORKDIR /usr/src/application
COPY ./ ./
RUN javac Solution.java
RUN jar cfe solution.jar Solution *.class
FROM gcr.io/distroless/java17-debian12:latest AS runner
FROM gcr.io/distroless/java21-debian12:latest AS runner
WORKDIR /usr/src/application
COPY --from=builder /usr/src/application/solution.jar ./
CMD ["./solution.jar"]

View File

@ -1,4 +1,4 @@
FROM gcr.io/distroless/nodejs20-debian12:latest AS runner
FROM gcr.io/distroless/nodejs22-debian12:latest AS runner
WORKDIR /usr/src/application
COPY ./ ./
CMD ["./solution.js"]

View File

@ -1,4 +1,4 @@
FROM pypy:3.10
FROM pypy:3.11-bookworm
WORKDIR /usr/src/application
COPY ./ ./
CMD ["python", "solution.py"]

View File

@ -1,4 +1,4 @@
FROM rust:1.75.0 AS builder
FROM rust:1.86.0 AS builder
WORKDIR /usr/src/rust_application
# Cache dependencies

View File

@ -1,15 +1,15 @@
FROM node:20.11.0 AS builder-dependencies
FROM node:22.15.0 AS builder-dependencies
WORKDIR /usr/src/application
COPY ./package*.json ./
RUN npm install
FROM node:20.11.0 AS builder
FROM node:22.15.0 AS builder
WORKDIR /usr/src/application
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
COPY ./ ./
RUN npm run build
FROM gcr.io/distroless/nodejs20-debian12:latest AS runner
FROM gcr.io/distroless/nodejs22-debian12:latest AS runner
WORKDIR /usr/src/application
ENV NODE_ENV=production
COPY --from=builder /usr/src/application/package.json ./package.json

View File

@ -4,7 +4,7 @@
"build": "tsc"
},
"devDependencies": {
"@types/node": "20.6.2",
"typescript": "5.2.2"
"@types/node": "22.9.0",
"typescript": "5.6.3"
}
}

View File

@ -16,6 +16,6 @@
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
},
"noUnusedParameters": true
}
}

View File

@ -1,15 +1,37 @@
{
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"lib": ["ESNext"],
"moduleResolution": "NodeNext",
"outDir": "./build",
"rootDir": "./cli",
"noEmit": true,
"checkJs": false,
"exactOptionalPropertyTypes": false,
"strict": true,
"allowUnusedLabels": false,
"allowUnreachableCode": false,
"noFallthroughCasesInSwitch": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noImplicitAny": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"esModuleInterop": true,
"allowImportingTsExtensions": false,
"skipLibCheck": true,
"jsx": "preserve",
"incremental": true,
"noEmit": true,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true
},
"exclude": ["node_modules", "challenges", "templates", "temp", "tmp"],
"exclude": ["node_modules", "challenges", "templates", "temp", "tmp"]
}