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:
parent
64c5d41358
commit
34644bd333
@ -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'))
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
from typing import List
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
maximum_number_of_cake_possible = None
|
maximum_number_of_cake_possible = None
|
||||||
|
@ -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))
|
||||||
|
|
||||||
|
@ -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(';')
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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]:
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
@ -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')))
|
||||||
|
|
||||||
|
@ -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')))
|
||||||
|
|
||||||
|
@ -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')))
|
||||||
|
|
||||||
|
@ -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')))
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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)
|
||||||
|
@ -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)) {
|
||||||
|
@ -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}`)
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
|
@ -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)) {
|
||||||
|
@ -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)
|
||||||
|
@ -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'))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user