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

Compare commits

..

No commits in common. "773f381f9f101873062d689bbd95651bb39e1802" and "2b6669bf4a4b3b4dfbf806cce4f3f2ec6dcfb337" have entirely different histories.

41 changed files with 1364 additions and 3366 deletions

View File

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

View File

@ -21,7 +21,7 @@ jobs:
SKIP_LOGIN: true SKIP_LOGIN: true
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.4.0" uses: "actions/setup-node@v4.1.0"
with: with:
node-version: "lts/*" node-version: "lts/*"
cache: "npm" cache: "npm"

View File

@ -13,7 +13,7 @@ jobs:
- uses: "actions/checkout@v4.2.2" - uses: "actions/checkout@v4.2.2"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.4.0" uses: "actions/setup-node@v4.1.0"
with: with:
node-version: "lts/*" node-version: "lts/*"
cache: "npm" cache: "npm"
@ -32,7 +32,7 @@ jobs:
- uses: "actions/checkout@v4.2.2" - uses: "actions/checkout@v4.2.2"
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.4.0" uses: "actions/setup-node@v4.1.0"
with: with:
node-version: "lts/*" node-version: "lts/*"
cache: "npm" cache: "npm"
@ -56,7 +56,7 @@ jobs:
SKIP_LOGIN: true SKIP_LOGIN: true
- name: "Setup Node.js" - name: "Setup Node.js"
uses: "actions/setup-node@v4.4.0" uses: "actions/setup-node@v4.1.0"
with: with:
node-version: "lts/*" node-version: "lts/*"
cache: "npm" cache: "npm"

3
.gitignore vendored
View File

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

12
.markdownlint-cli2.jsonc Normal file
View File

@ -0,0 +1,12 @@
{
"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"],
}

View File

@ -1,19 +0,0 @@
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 Normal file
View File

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

View File

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

View File

@ -80,20 +80,17 @@ programming-challenges --help
# Generate a new challenge # Generate a new challenge
programming-challenges generate challenge --github-user="YourGitHubName" --challenge="hello-world" 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 # Generate a new solution
programming-challenges generate solution --github-user="YourGitHubName" --challenge="hello-world" --solution="function" --language="python" programming-challenges generate solution --github-user="YourGitHubName" --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
# Test a solution # Test a solution
programming-challenges run test --challenge="hello-world" --solution="function" --language="python" programming-challenges run test --challenge="hello-world" --solution="function" --language="python"
# Test all the solutions in all the challenges # Run a solution with specific `input.txt` file
programming-challenges run test --all 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"
``` ```
## 💡 Contributing ## 💡 Contributing

View File

@ -1,25 +0,0 @@
# 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

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

View File

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

View File

@ -1,16 +0,0 @@
#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

@ -1,14 +0,0 @@
#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

@ -1,16 +0,0 @@
#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

@ -1,40 +0,0 @@
#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

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

View File

@ -1 +0,0 @@
2

View File

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

View File

@ -1 +0,0 @@
0

View File

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

View File

@ -1 +0,0 @@
12

View File

@ -16,7 +16,9 @@ make?
### Input ### Input
- **Line 1:** Single integer `N` for the number of ingredients. - **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 ### Output
@ -30,7 +32,7 @@ available ingredients.
## Source ## Source
[SWERC 2020-2021 - Problem E: Cake](https://swerc.eu/2020/problems/) [SWERC 20202021 - Problem E: Cake](https://swerc.eu/2020/problems/)
## Examples ## Examples

View File

@ -21,7 +21,6 @@ This data is comprised of lines, each of which represents a defibrillator. Each
- Contact Phone number - Contact Phone number
- Longitude (degrees) - Longitude (degrees)
- Latitude (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. **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,3 +14,37 @@ Display a pyramid of stars (`*`) whose height is given and in the right order (`
## Examples ## Examples
See the `test` folder for examples of input/output. 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

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

View File

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

View File

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

View File

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

View File

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

4311
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -27,41 +27,42 @@
"test": "cross-env NODE_ENV=test node --enable-source-maps --test \"build/**/*.test.js\"" "test": "cross-env NODE_ENV=test node --enable-source-maps --test \"build/**/*.test.js\""
}, },
"dependencies": { "dependencies": {
"chalk": "5.4.1", "chalk": "5.3.0",
"clipanion": "3.2.1", "clipanion": "3.2.1",
"date-and-time": "3.6.0", "date-and-time": "3.6.0",
"execa": "9.5.3", "execa": "9.5.1",
"log-symbols": "7.0.0", "log-symbols": "6.0.0",
"ora": "8.2.0", "ora": "8.1.1",
"replace-in-file": "8.3.0", "replace-in-file": "8.2.0",
"table": "6.9.0", "table": "6.8.2",
"typanion": "3.14.0", "typanion": "3.14.0",
"validate-npm-package-name": "6.0.0" "validate-npm-package-name": "6.0.0"
}, },
"devDependencies": { "devDependencies": {
"@swc/cli": "0.7.7", "@swc/cli": "0.5.0",
"@swc/core": "1.11.24", "@swc/core": "1.9.2",
"@tsconfig/strictest": "2.0.5",
"@types/mock-fs": "4.13.4", "@types/mock-fs": "4.13.4",
"@types/ms": "2.1.0", "@types/ms": "0.7.34",
"@types/node": "22.15.17", "@types/node": "22.9.0",
"@types/sinon": "17.0.4", "@types/sinon": "17.0.3",
"@types/validate-npm-package-name": "4.0.2", "@types/validate-npm-package-name": "4.0.2",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"editorconfig-checker": "6.0.1", "editorconfig-checker": "6.0.0",
"eslint": "9.26.0", "eslint": "9.15.0",
"eslint-config-conventions": "19.2.0", "eslint-config-conventions": "17.0.1",
"eslint-plugin-import-x": "4.11.1", "eslint-plugin-import-x": "4.4.2",
"eslint-plugin-promise": "7.2.1", "eslint-plugin-promise": "7.1.0",
"eslint-plugin-unicorn": "59.0.1", "eslint-plugin-unicorn": "56.0.0",
"get-stream": "9.0.1", "get-stream": "9.0.1",
"globals": "16.1.0", "globals": "15.12.0",
"markdownlint-cli2": "0.18.0", "markdownlint-cli2": "0.15.0",
"markdownlint-rule-relative-links": "4.1.0", "markdownlint-rule-relative-links": "3.0.0",
"mock-fs": "5.5.0", "mock-fs": "5.4.1",
"ms": "2.1.3", "ms": "2.1.3",
"prettier": "3.5.3", "prettier": "3.3.3",
"sinon": "20.0.0", "sinon": "19.0.2",
"typescript": "5.8.3", "typescript": "5.6.3",
"typescript-eslint": "8.32.1" "typescript-eslint": "8.14.0"
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
FROM node:22.15.0 AS builder-dependencies FROM node:22.11.0 AS builder-dependencies
WORKDIR /usr/src/application WORKDIR /usr/src/application
COPY ./package*.json ./ COPY ./package*.json ./
RUN npm install RUN npm install
FROM node:22.15.0 AS builder FROM node:22.11.0 AS builder
WORKDIR /usr/src/application WORKDIR /usr/src/application
COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules COPY --from=builder-dependencies /usr/src/application/node_modules ./node_modules
COPY ./ ./ COPY ./ ./

View File

@ -1,37 +1,15 @@
{ {
"extends": "@tsconfig/strictest/tsconfig.json",
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"lib": ["ESNext"], "lib": ["ESNext"],
"moduleResolution": "NodeNext",
"outDir": "./build", "outDir": "./build",
"rootDir": "./cli", "rootDir": "./cli",
"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, "noEmit": true,
"checkJs": false,
"target": "ESNext", "exactOptionalPropertyTypes": false
"module": "ESNext",
"moduleResolution": "Bundler",
"resolveJsonModule": true
}, },
"exclude": ["node_modules", "challenges", "templates", "temp", "tmp"] "exclude": ["node_modules", "challenges", "templates", "temp", "tmp"]
} }