1
1
mirror of https://github.com/theoludwig/programming-challenges.git synced 2024-12-08 00:45:29 +01:00

refactor: usage of built-in type hinting in Python solutions

This commit is contained in:
Divlo 2022-04-24 20:27:51 +02:00
parent 64c5d41358
commit 34644bd333
No known key found for this signature in database
GPG Key ID: 8F9478F220CE65E9
27 changed files with 142 additions and 116 deletions

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,7 +1,7 @@
from typing import List, TypedDict from typing import TypedDict
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))
@ -11,8 +11,8 @@ class ShiftedLetter(TypedDict):
shifted: str shifted: str
def shift_alphabet(shift: int) -> List[ShiftedLetter]: def shift_alphabet(shift: int) -> list[ShiftedLetter]:
result: List[ShiftedLetter] = [] result: list[ShiftedLetter] = []
alphabet = 'A B C D E F G H I J K L M N O P Q R S T U V W X Y Z'.split(' ') alphabet = 'A B C D E F G H I J K L M N O P Q R S T U V W X Y Z'.split(' ')
is_negative_shift = shift < 0 is_negative_shift = shift < 0
if is_negative_shift: if is_negative_shift:

View File

@ -1,4 +1,3 @@
from typing import List
import sys import sys
maximum_number_of_cake_possible = None maximum_number_of_cake_possible = None

View File

@ -1,16 +1,15 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))
def consecutive_numbers(numbers: List[int], couple_length: int) -> List[List[int]]: def consecutive_numbers(numbers: list[int], couple_length: int) -> list[list[int]]:
result: List[List[int]] = [] result: list[list[int]] = []
numbers_length = len(numbers) numbers_length = len(numbers)
for index in range(numbers_length): for index in range(numbers_length):
consecutive: List[int] = [numbers[index]] consecutive: list[int] = [numbers[index]]
for couple_index in range(1, couple_length, 1): for couple_index in range(1, couple_length, 1):
is_last_number = index + couple_index == numbers_length is_last_number = index + couple_index == numbers_length
if is_last_number: if is_last_number:
@ -23,7 +22,7 @@ def consecutive_numbers(numbers: List[int], couple_length: int) -> List[List[int
return result return result
numbers: List[int] = [] numbers: list[int] = []
for value in input_values[1].split(' ; '): for value in input_values[1].split(' ; '):
numbers.append(int(value)) numbers.append(int(value))

View File

@ -1,8 +1,7 @@
from typing import List
import sys import sys
import math import math
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))
@ -28,7 +27,7 @@ class Position:
class Defibrillator: class Defibrillator:
def __init__(self, strings: List[str], user_position: Position) -> None: def __init__(self, strings: list[str], user_position: Position) -> None:
self.id = strings[0] self.id = strings[0]
self.name = strings[1] self.name = strings[1]
self.address = strings[2] self.address = strings[2]
@ -41,7 +40,7 @@ class Defibrillator:
longitude = convert_string_to_float(input_values[0]) longitude = convert_string_to_float(input_values[0])
latitude = convert_string_to_float(input_values[1]) latitude = convert_string_to_float(input_values[1])
user_position = Position(longitude, latitude) user_position = Position(longitude, latitude)
defibrillators: List[Defibrillator] = [] defibrillators: list[Defibrillator] = []
for index in range(3, len(input_values), 1): for index in range(3, len(input_values), 1):
line = input_values[index].split(';') line = input_values[index].split(';')

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,13 +1,12 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))
def divider_list(number: int) -> List[int]: def divider_list(number: int) -> list[int]:
number_list: List[int] = [] number_list: list[int] = []
for index in range(1, number + 1): for index in range(1, number + 1):
if number % index == 0: if number % index == 0:
number_list.append(index) number_list.append(index)

View File

@ -1,12 +1,11 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))
def get_is_valid_subsequence(array: List, sequence: List): def get_is_valid_subsequence(array: list, sequence: list):
index_to_check = 0 index_to_check = 0
for index in range(len(array)): for index in range(len(array)):
if index_to_check < len(sequence) and array[index] == sequence[index_to_check]: if index_to_check < len(sequence) and array[index] == sequence[index_to_check]:

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,7 +1,13 @@
from typing import List from typing import TypedDict
import sys import sys
matches_roman_arabic = [
class RomanArabicMatch(TypedDict):
arabic: int
roman: str
matches_roman_arabic: list[RomanArabicMatch] = [
{'arabic': 1000, 'roman': 'M'}, {'arabic': 1000, 'roman': 'M'},
{'arabic': 900, 'roman': 'CM'}, {'arabic': 900, 'roman': 'CM'},
{'arabic': 500, 'roman': 'D'}, {'arabic': 500, 'roman': 'D'},
@ -42,7 +48,7 @@ def convert_roman_to_arabic(roman_number: str) -> int:
return arabic_number return arabic_number
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))

View File

@ -1,8 +1,7 @@
from typing import List
import sys import sys
def bubble_sort(numbersInput: List[int]) -> List[int]: def bubble_sort(numbersInput: list[int]) -> list[int]:
numbers = list(numbersInput) numbers = list(numbersInput)
length = len(numbers) length = len(numbers)
for index_1 in range(length): for index_1 in range(length):
@ -14,7 +13,7 @@ def bubble_sort(numbersInput: List[int]) -> List[int]:
return numbers return numbers
numbers: List[int] = [] numbers: list[int] = []
for value in sys.stdin: for value in sys.stdin:
numbers.append(int(value.rstrip('\n'))) numbers.append(int(value.rstrip('\n')))

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
numbers: List[int] = [] numbers: list[int] = []
for value in sys.stdin: for value in sys.stdin:
numbers.append(int(value.rstrip('\n'))) numbers.append(int(value.rstrip('\n')))

View File

@ -1,8 +1,7 @@
from typing import List
import sys import sys
def insertion_sort(numbersInput: List[int]) -> List[int]: def insertion_sort(numbersInput: list[int]) -> list[int]:
numbers = list(numbersInput) numbers = list(numbersInput)
for index_1 in range(1, len(numbers)): for index_1 in range(1, len(numbers)):
current = numbers[index_1] current = numbers[index_1]
@ -14,7 +13,7 @@ def insertion_sort(numbersInput: List[int]) -> List[int]:
return numbers return numbers
numbers: List[int] = [] numbers: list[int] = []
for value in sys.stdin: for value in sys.stdin:
numbers.append(int(value.rstrip('\n'))) numbers.append(int(value.rstrip('\n')))

View File

@ -1,20 +1,22 @@
from typing import List, Any from typing import TypeVar
import sys import sys
T = TypeVar('T')
def divide_list(values: List[Any]) -> List[Any]:
def divide_list(values: list[T]) -> tuple[list[T], list[T]]:
middle = len(values) // 2 middle = len(values) // 2
left = values[middle:] left = values[middle:]
right = values[:middle] right = values[:middle]
return [left, right] return (left, right)
def merge(numbers_1: List[int], numbers_2: List[int]) -> List[int]: def merge(numbers_1: list[int], numbers_2: list[int]) -> list[int]:
length_numbers_1 = len(numbers_1) length_numbers_1 = len(numbers_1)
length_numbers_2 = len(numbers_2) length_numbers_2 = len(numbers_2)
index_numbers_1 = 0 index_numbers_1 = 0
index_numbers_2 = 0 index_numbers_2 = 0
result: List[int] = [] result: list[int] = []
while index_numbers_1 < length_numbers_1 and index_numbers_2 < length_numbers_2: while index_numbers_1 < length_numbers_1 and index_numbers_2 < length_numbers_2:
if numbers_1[index_numbers_1] < numbers_2[index_numbers_2]: if numbers_1[index_numbers_1] < numbers_2[index_numbers_2]:
result.append(numbers_1[index_numbers_1]) result.append(numbers_1[index_numbers_1])
@ -29,7 +31,7 @@ def merge(numbers_1: List[int], numbers_2: List[int]) -> List[int]:
return result return result
def merge_sort(numbers: List[int]) -> List[int]: def merge_sort(numbers: list[int]) -> list[int]:
if len(numbers) <= 1: if len(numbers) <= 1:
return numbers return numbers
left, right = divide_list(numbers) left, right = divide_list(numbers)
@ -37,7 +39,7 @@ def merge_sort(numbers: List[int]) -> List[int]:
return merge(left, right) return merge(left, right)
numbers: List[int] = [] numbers: list[int] = []
for value in sys.stdin: for value in sys.stdin:
numbers.append(int(value.rstrip('\n'))) numbers.append(int(value.rstrip('\n')))

View File

@ -1,13 +1,11 @@
from typing import List
from Cell import Cell from Cell import Cell
class Grid: class Grid:
def __init__(self, grid: List[List[int]]) -> None: def __init__(self, grid: list[list[int]]) -> None:
data: List[List[Cell]] = [] data: list[list[Cell]] = []
for x in range(len(grid)): for x in range(len(grid)):
column: List[Cell] = [] column: list[Cell] = []
for y in range(len(grid[x])): for y in range(len(grid[x])):
column.append(Cell(grid[x][y], y, x)) column.append(Cell(grid[x][y], y, x))
data.append(column) data.append(column)

View File

@ -1,13 +1,12 @@
from typing import List
import sys import sys
from Sudoku import Sudoku from Sudoku import Sudoku
from Grid import Grid from Grid import Grid
grid_values: List[List[int]] = [] grid_values: list[list[int]] = []
for value in sys.stdin: for value in sys.stdin:
row_values = value.rstrip('\n').split(' ') row_values = value.rstrip('\n').split(' ')
current_row: List[int] = [] current_row: list[int] = []
for row_value in row_values: for row_value in row_values:
current_row.append(int(row_value)) current_row.append(int(row_value))
grid_values.append(current_row) grid_values.append(current_row)

View File

@ -23,7 +23,7 @@ export class GenerateChallengeCommand extends Command {
validator: typanion.isString() validator: typanion.isString()
}) })
async execute (): Promise<number> { async execute(): Promise<number> {
try { try {
const challenge = await Challenge.generate({ const challenge = await Challenge.generate({
name: this.challenge, name: this.challenge,

View File

@ -35,7 +35,7 @@ export class GenerateSolutionCommand extends Command {
validator: typanion.isString() validator: typanion.isString()
}) })
async execute (): Promise<number> { async execute(): Promise<number> {
try { try {
const solution = await Solution.generate({ const solution = await Solution.generate({
name: this.solutionName, name: this.solutionName,

View File

@ -46,11 +46,13 @@ export class RunTestCommand extends Command {
description: 'Base of the current branch (usually master)' description: 'Base of the current branch (usually master)'
}) })
async execute (): Promise<number> { async execute(): Promise<number> {
console.log() console.log()
try { try {
if (this.programmingLanguage != null) { if (this.programmingLanguage != null) {
await template.verifySupportedProgrammingLanguage(this.programmingLanguage) await template.verifySupportedProgrammingLanguage(
this.programmingLanguage
)
} }
if (this.all) { if (this.all) {
return await Test.runAllTests(this.programmingLanguage) return await Test.runAllTests(this.programmingLanguage)

View File

@ -18,13 +18,15 @@ export class Challenge implements ChallengeOptions {
public name: string public name: string
public path: string public path: string
constructor (options: ChallengeOptions) { constructor(options: ChallengeOptions) {
const { name } = options const { name } = options
this.name = name this.name = name
this.path = fileURLToPath(new URL(`../../challenges/${name}`, import.meta.url)) this.path = fileURLToPath(
new URL(`../../challenges/${name}`, import.meta.url)
)
} }
static async generate (options: GenerateChallengeOptions): Promise<Challenge> { static async generate(options: GenerateChallengeOptions): Promise<Challenge> {
const { name, githubUser } = options const { name, githubUser } = options
const challenge = new Challenge({ name }) const challenge = new Challenge({ name })
if (await isExistingPath(challenge.path)) { if (await isExistingPath(challenge.path)) {

View File

@ -8,7 +8,7 @@ export class Docker {
static MAXIMUM_TIMEOUT = '1 minute' static MAXIMUM_TIMEOUT = '1 minute'
static MAXIMUM_TIMEOUT_MILLISECONDS = ms(Docker.MAXIMUM_TIMEOUT) static MAXIMUM_TIMEOUT_MILLISECONDS = ms(Docker.MAXIMUM_TIMEOUT)
public async build (): Promise<void> { public async build(): Promise<void> {
const loader = ora('Building the Docker image').start() const loader = ora('Building the Docker image').start()
try { try {
await execaCommand(`docker build --tag=${Docker.CONTAINER_TAG} ./`) await execaCommand(`docker build --tag=${Docker.CONTAINER_TAG} ./`)
@ -19,7 +19,7 @@ export class Docker {
} }
} }
public async run (input: string): Promise<string> { public async run(input: string): Promise<string> {
const subprocess = execaCommand( const subprocess = execaCommand(
`docker run --interactive --rm ${Docker.CONTAINER_TAG}`, `docker run --interactive --rm ${Docker.CONTAINER_TAG}`,
{ {
@ -40,10 +40,14 @@ export class Docker {
return stdout return stdout
} catch (error: any) { } catch (error: any) {
if (!isValid) { if (!isValid) {
throw new Error(`Timeout: time limit exceeded (${Docker.MAXIMUM_TIMEOUT}), try to optimize your solution.`) throw new Error(
`Timeout: time limit exceeded (${Docker.MAXIMUM_TIMEOUT}), try to optimize your solution.`
)
} }
if (error.exitCode === Docker.SIGSEGV_EXIT_CODE) { if (error.exitCode === Docker.SIGSEGV_EXIT_CODE) {
throw new Error('Docker run failed: SIGSEGV indicates a segmentation fault (attempts to access a memory location that it\'s not allowed to access).') throw new Error(
"Docker run failed: SIGSEGV indicates a segmentation fault (attempts to access a memory location that it's not allowed to access)."
)
} }
throw new Error(`Docker run failed: ${error.message as string}`) throw new Error(`Docker run failed: ${error.message as string}`)
} }

View File

@ -3,11 +3,14 @@ import { execaCommand } from 'execa'
import { Challenge } from './Challenge.js' import { Challenge } from './Challenge.js'
import { Solution } from './Solution.js' import { Solution } from './Solution.js'
const solutionsRegex = /challenges\/[\S\s]*\/solutions\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/[\S\s]*\/(.*).(c|cpp|cs|dart|java|js|py|rs|ts)/ const solutionsRegex =
/challenges\/[\S\s]*\/solutions\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/[\S\s]*\/(.*).(c|cpp|cs|dart|java|js|py|rs|ts)/
const dockerRegex = /templates\/docker\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/(.*)/ const dockerRegex =
/templates\/docker\/(c|cpp|cs|dart|java|javascript|python|rust|typescript)\/(.*)/
const inputOutputRegex = /challenges\/[\S\s]*\/test\/(.*)\/(input.txt|output.txt)/ const inputOutputRegex =
/challenges\/[\S\s]*\/test\/(.*)\/(input.txt|output.txt)/
export interface GitAffectedOptions { export interface GitAffectedOptions {
isContinuousIntegration: boolean isContinuousIntegration: boolean
@ -18,19 +21,19 @@ export class GitAffected implements GitAffectedOptions {
public isContinuousIntegration: boolean public isContinuousIntegration: boolean
public base?: string public base?: string
constructor (options: GitAffectedOptions) { constructor(options: GitAffectedOptions) {
this.isContinuousIntegration = options.isContinuousIntegration this.isContinuousIntegration = options.isContinuousIntegration
this.base = options.base this.base = options.base
} }
public parseGitOutput (output: string): string[] { public parseGitOutput(output: string): string[] {
return output return output
.split('\n') .split('\n')
.map((line) => line.trim()) .map((line) => line.trim())
.filter((line) => line.length > 0) .filter((line) => line.length > 0)
} }
public async getFilesUsingBaseAndHead ( public async getFilesUsingBaseAndHead(
base: string, base: string,
head: string head: string
): Promise<string[]> { ): Promise<string[]> {
@ -44,24 +47,29 @@ export class GitAffected implements GitAffectedOptions {
} }
} }
public async getUncommittedFiles (): Promise<string[]> { public async getUncommittedFiles(): Promise<string[]> {
return await this.getFilesUsingBaseAndHead('HEAD', '.') return await this.getFilesUsingBaseAndHead('HEAD', '.')
} }
public async getLatestPushedCommit (): Promise<string> { public async getLatestPushedCommit(): Promise<string> {
const latestCommit = this.isContinuousIntegration ? '~1' : '' const latestCommit =
const { stdout } = await execaCommand(`git rev-parse origin/master${latestCommit}`) this.isContinuousIntegration || this.base != null ? '~1' : ''
const { stdout } = await execaCommand(
`git rev-parse origin/master${latestCommit}`
)
return stdout return stdout
} }
public async getUnpushedFiles (): Promise<string[]> { public async getUnpushedFiles(): Promise<string[]> {
return await this.getFilesUsingBaseAndHead( return await this.getFilesUsingBaseAndHead(
await this.getLatestPushedCommit(), await this.getLatestPushedCommit(),
'.' '.'
) )
} }
public async getAffectedSolutionsFromFiles (files: string[]): Promise<Solution[]> { public async getAffectedSolutionsFromFiles(
files: string[]
): Promise<Solution[]> {
const affectedSolutionsPaths = files.filter((filePath) => { const affectedSolutionsPaths = files.filter((filePath) => {
return solutionsRegex.test(filePath) return solutionsRegex.test(filePath)
}) })
@ -69,18 +77,26 @@ export class GitAffected implements GitAffectedOptions {
return dockerRegex.test(filePath) return dockerRegex.test(filePath)
}) })
const affectedLanguages = affectedDockerPaths.map((filePath) => { const affectedLanguages = affectedDockerPaths.map((filePath) => {
const [,, programmingLanguageName] = filePath.replaceAll('\\', '/').split('/') const [, , programmingLanguageName] = filePath
.replaceAll('\\', '/')
.split('/')
return programmingLanguageName return programmingLanguageName
}) })
const affectedInputOutput = files.filter((filePath) => { const affectedInputOutput = files.filter((filePath) => {
return inputOutputRegex.test(filePath) return inputOutputRegex.test(filePath)
}) })
const affectedChallengesFromInputOutput = affectedInputOutput.map((filePath) => { const affectedChallengesFromInputOutput = affectedInputOutput.map(
const [, challengeName] = filePath.replaceAll('\\', '/').split('/') (filePath) => {
return new Challenge({ name: challengeName }) const [, challengeName] = filePath.replaceAll('\\', '/').split('/')
}) return new Challenge({ name: challengeName })
const solutionsChallenges = await Solution.getManyByPaths(affectedSolutionsPaths) }
const solutionsDocker = await Solution.getManyByProgrammingLanguages(affectedLanguages) )
const solutionsChallenges = await Solution.getManyByPaths(
affectedSolutionsPaths
)
const solutionsDocker = await Solution.getManyByProgrammingLanguages(
affectedLanguages
)
const solutions: Solution[] = [...solutionsDocker, ...solutionsChallenges] const solutions: Solution[] = [...solutionsDocker, ...solutionsChallenges]
for (const challenge of affectedChallengesFromInputOutput) { for (const challenge of affectedChallengesFromInputOutput) {
const solutionsByChallenge = await Solution.getManyByChallenge(challenge) const solutionsByChallenge = await Solution.getManyByChallenge(challenge)
@ -98,7 +114,7 @@ export class GitAffected implements GitAffectedOptions {
return solutionsUnique return solutionsUnique
} }
public async getAffectedSolutionsFromGit (): Promise<Solution[]> { public async getAffectedSolutionsFromGit(): Promise<Solution[]> {
let files = [ let files = [
...(await this.getUnpushedFiles()), ...(await this.getUnpushedFiles()),
...(await this.getUncommittedFiles()) ...(await this.getUncommittedFiles())

View File

@ -35,7 +35,7 @@ export class Solution implements SolutionOptions {
public name: string public name: string
public path: string public path: string
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
@ -48,7 +48,7 @@ export class Solution implements SolutionOptions {
) )
} }
private async prepareTemporaryFolder (): Promise<void> { private async prepareTemporaryFolder(): Promise<void> {
await createTemporaryEmptyFolder() await createTemporaryEmptyFolder()
await copyDirectory(this.path, TEMPORARY_PATH) await copyDirectory(this.path, TEMPORARY_PATH)
await template.docker({ await template.docker({
@ -58,13 +58,13 @@ export class Solution implements SolutionOptions {
process.chdir(TEMPORARY_PATH) process.chdir(TEMPORARY_PATH)
} }
public async test (): Promise<void> { public async test(): Promise<void> {
await this.prepareTemporaryFolder() await this.prepareTemporaryFolder()
await docker.build() await docker.build()
await Test.runAll(this) await Test.runAll(this)
} }
static async generate (options: GenerateSolutionOptions): Promise<Solution> { static async generate(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))) {
@ -88,7 +88,7 @@ export class Solution implements SolutionOptions {
return solution return solution
} }
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
@ -104,7 +104,7 @@ export class Solution implements SolutionOptions {
return solution return solution
} }
static async getManyByChallenge (challenge: Challenge): Promise<Solution[]> { static async getManyByChallenge(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) => {
@ -113,7 +113,9 @@ export class Solution implements SolutionOptions {
) )
const paths: string[] = [] const paths: string[] = []
for (const language of languagesSolution) { for (const language of languagesSolution) {
const solutionPath = (await fs.promises.readdir(path.join(solutionsPath, language))).map((solutionName) => { const solutionPath = (
await fs.promises.readdir(path.join(solutionsPath, language))
).map((solutionName) => {
return `challenges/${challenge.name}/solutions/${language}/${solutionName}` return `challenges/${challenge.name}/solutions/${language}/${solutionName}`
}) })
paths.push(...solutionPath) paths.push(...solutionPath)
@ -121,20 +123,27 @@ export class Solution implements SolutionOptions {
return await Solution.getManyByPaths(paths) return await Solution.getManyByPaths(paths)
} }
static async getManyByProgrammingLanguages (programmingLanguages?: string[]): Promise<Solution[]> { static async getManyByProgrammingLanguages(
const languages = programmingLanguages ?? await template.getProgrammingLanguages() programmingLanguages?: string[]
const challengesPath = fileURLToPath(new URL('../../challenges', import.meta.url)) ): Promise<Solution[]> {
const languages =
programmingLanguages ?? (await template.getProgrammingLanguages())
const challengesPath = fileURLToPath(
new URL('../../challenges', import.meta.url)
)
const challenges = await fs.promises.readdir(challengesPath) const challenges = await fs.promises.readdir(challengesPath)
const paths: string[] = [] const paths: string[] = []
for (const challenge of challenges) { for (const challenge of challenges) {
const solutionsPath = path.join(challengesPath, challenge, 'solutions') const solutionsPath = path.join(challengesPath, challenge, 'solutions')
const languagesSolution = (await fs.promises.readdir(solutionsPath)).filter( const languagesSolution = (
(name) => { await fs.promises.readdir(solutionsPath)
return name !== '.gitkeep' && languages.includes(name) ).filter((name) => {
} return name !== '.gitkeep' && languages.includes(name)
) })
for (const language of languagesSolution) { for (const language of languagesSolution) {
const solutionPath = (await fs.promises.readdir(path.join(solutionsPath, language))).map((solutionName) => { const solutionPath = (
await fs.promises.readdir(path.join(solutionsPath, language))
).map((solutionName) => {
return `challenges/${challenge}/solutions/${language}/${solutionName}` return `challenges/${challenge}/solutions/${language}/${solutionName}`
}) })
paths.push(...solutionPath) paths.push(...solutionPath)
@ -148,7 +157,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
*/ */
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

@ -39,7 +39,7 @@ export class Test implements TestOptions {
public elapsedTimeMilliseconds: number public elapsedTimeMilliseconds: number
static SUCCESS_MESSAGE = `${chalk.bold.green('Success:')} Tests passed! 🎉` static SUCCESS_MESSAGE = `${chalk.bold.green('Success:')} Tests passed! 🎉`
constructor (options: TestOptions) { constructor(options: TestOptions) {
this.index = options.index this.index = options.index
this.path = options.path this.path = options.path
this.isSuccess = options.isSuccess this.isSuccess = options.isSuccess
@ -49,7 +49,7 @@ export class Test implements TestOptions {
this.elapsedTimeMilliseconds = options.elapsedTimeMilliseconds this.elapsedTimeMilliseconds = options.elapsedTimeMilliseconds
} }
static printResult (tests: Test[]): void { static printResult(tests: Test[]): void {
const tableResult = [ const tableResult = [
[ [
chalk.bold('N°'), chalk.bold('N°'),
@ -95,11 +95,13 @@ export class Test implements TestOptions {
} }
} }
static async runAll (solution: Solution): Promise<void> { static async runAll(solution: Solution): Promise<void> {
const name = `${solution.challenge.name}/${solution.programmingLanguageName}/${solution.name}` const name = `${solution.challenge.name}/${solution.programmingLanguageName}/${solution.name}`
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) => Number(test)).sort((a, b) => a - b) const testsNumbers = testsFolders
.map((test) => Number(test))
.sort((a, b) => a - b)
const tests: Test[] = [] const tests: Test[] = []
console.log(`${chalk.bold('Name:')} ${name}\n`) console.log(`${chalk.bold('Name:')} ${name}\n`)
for (const testNumber of testsNumbers) { for (const testNumber of testsNumbers) {
@ -123,7 +125,7 @@ export class Test implements TestOptions {
Test.printResult(tests) Test.printResult(tests)
} }
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' })
@ -133,7 +135,7 @@ export class Test implements TestOptions {
return { input, output } return { input, output }
} }
static async runManyWithSolutions (solutions: Solution[]): Promise<number> { static async runManyWithSolutions(solutions: Solution[]): Promise<number> {
for (const solution of solutions) { for (const solution of solutions) {
await solution.test() await solution.test()
console.log('\n------------------------------\n') console.log('\n------------------------------\n')
@ -142,7 +144,7 @@ export class Test implements TestOptions {
return 0 return 0
} }
static async runAllTests (programmingLanguage?: string): Promise<number> { static async runAllTests(programmingLanguage?: string): Promise<number> {
const solutions = await Solution.getManyByProgrammingLanguages( const solutions = await Solution.getManyByProgrammingLanguages(
programmingLanguage != null ? [programmingLanguage] : undefined programmingLanguage != null ? [programmingLanguage] : undefined
) )
@ -150,7 +152,7 @@ export class Test implements TestOptions {
return 0 return 0
} }
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)
const start = performance.now() const start = performance.now()
const stdout = await docker.run(input) const stdout = await docker.run(input)

View File

@ -1,7 +1,6 @@
from typing import List
import sys import sys
input_values: List[str] = [] input_values: list[str] = []
for value in sys.stdin: for value in sys.stdin:
input_values.append(value.rstrip('\n')) input_values.append(value.rstrip('\n'))