Areplay:alfa
31/32
Breplay:beta
30/32
tests superados
gana A por tests superados (31 frente a 30, con las mismas tareas resueltas)
2/3
Tareas resueltas
todos los tests en todas las ejecuciones
2/3
31/32(mejor)
Tests superados
(peor)30/32
10,1 s(mejor)tests: 1,3 s
Tiempo del modelo
menos es mejor
(peor)28,1 stests: 1,3 s
2.104(mejor)entrada 1.401 · salida 703
Tokens
menos es mejor
(peor)3.505entrada 1.373 · salida 2.132
0,0017 USD(mejor)
Coste estimado
precios ficticios de demostración
(peor)0,0248 USD
Tests superados por tarea
Tabla por tarea
| Tarea | Tests | Tiempo del modelo | Tokens entrada / salida | Coste | ||||
|---|---|---|---|---|---|---|---|---|
| A | B | A | B | A | B | A | B | |
| Convertir un texto en slugfácil | 8/8 | 6/8 | 2,8 s | 7,6 s | 412 / 188 | 405 / 596 | 0,0005 USD | 0,0070 USD |
| Fusionar intervalosmedia | 8/8 | 8/8 | 3,4 s | 8,9 s | 468 / 241 | 459 / 702 | 0,0006 USD | 0,0082 USD |
| Convertir una duración a segundosdifícil | 15/16 | 16/16 | 3,9 s | 11,5 s | 521 / 274 | 509 / 834 | 0,0006 USD | 0,0096 USD |
Código y salida de los tests
Convertir un texto en slugslugifyA 8/8 B 6/8
# Convertir un texto en slug
Escribe una función de Python con esta firma exacta:
```python
def slugify(text: str, separator: str = "-") -> str:
```
Debe convertir cualquier texto en un *slug* apto para una URL siguiendo estas reglas:
1. El resultado va en minúsculas.
2. Las letras con tilde, diéresis u otros diacríticos se convierten en su letra base
(`á` → `a`, `ñ` → `n`, `ü` → `u`, `ç` → `c`).
3. Solo se conservan letras ASCII (`a`-`z`) y dígitos (`0`-`9`).
4. Cualquier secuencia de uno o más caracteres no conservados (espacios, signos de
puntuación, barras, guiones, puntos…) se sustituye por **un único** `separator`.
5. El resultado nunca empieza ni termina con el separador.
6. Si no queda ningún carácter conservado, devuelve la cadena vacía `""`.
Ejemplos:
- `slugify("Canción de Añoranza")` → `"cancion-de-anoranza"`
- `slugify("Python 3.12 en 2026")` → `"python-3-12-en-2026"`
- `slugify("Árbol Genealógico", separator="_")` → `"arbol_genealogico"`
A · replay:alfa
8/8 tests · resuelta · 2,8 s
import re
import unicodedata
def slugify(text: str, separator: str = "-") -> str:
normalized = unicodedata.normalize("NFKD", text)
ascii_text = normalized.encode("ascii", "ignore").decode("ascii").lower()
parts = re.split(r"[^a-z0-9]+", ascii_text)
return separator.join(part for part in parts if part)
Salida de pytest
........ [100%] 8 passed in 0.06s
B · replay:beta
6/8 tests · 2 fallan · 7,6 s
import re
import unicodedata
def slugify(text: str, separator: str = "-") -> str:
"""Convierte ``text`` en un slug apto para URL."""
# 1. Quitar tildes y diacríticos: "Canción" -> "Cancion".
text = unicodedata.normalize("NFKD", text)
text = text.encode("ascii", "ignore").decode("ascii")
# 2. Eliminar todo lo que no sea letra, dígito, espacio o guion.
text = re.sub(r"[^\w\s-]", "", text).strip().lower()
# 3. Colapsar espacios, guiones y guiones bajos en un único separador.
text = re.sub(r"[\s_-]+", separator, text)
# 4. Nunca empezar ni terminar con el separador.
return text.strip(separator)
Salida de pytest
....FF.. [100%]
================================== FAILURES ===================================
_______________________ test_punto_entre_digitos_separa _______________________
test_task.py:22: in test_punto_entre_digitos_separa
assert slugify("Python 3.12 en 2026") == "python-3-12-en-2026"
E AssertionError: assert 'python-312-en-2026' == 'python-3-12-en-2026'
E
E - python-3-12-en-2026
E ? -
E + python-312-en-2026
_______________________ test_barra_y_guion_bajo_separan _______________________
test_task.py:26: in test_barra_y_guion_bajo_separan
assert slugify("Entrada/Salida") == "entrada-salida"
E AssertionError: assert 'entradasalida' == 'entrada-salida'
E
E - entrada-salida
E ? -
E + entradasalida
=========================== short test summary info ===========================
FAILED test_task.py::test_punto_entre_digitos_separa - AssertionError: assert...
FAILED test_task.py::test_barra_y_guion_bajo_separan - AssertionError: assert...
2 failed, 6 passed in 0.15sFusionar intervalosmerge_intervalsA 8/8 B 8/8
# Fusionar intervalos
Escribe una función de Python con esta firma exacta:
```python
def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
```
Recibe una lista de intervalos **cerrados** `(inicio, fin)` de números enteros y devuelve
una lista nueva con los intervalos fusionados:
1. El resultado está ordenado por `inicio` y ningún par de intervalos del resultado se
solapa ni se toca.
2. Dos intervalos que se solapan o que **comparten un extremo** se fusionan:
`(1, 3)` y `(3, 5)` → `(1, 5)`.
3. Dos intervalos consecutivos que no comparten ningún punto **no** se fusionan:
`(1, 2)` y `(3, 4)` siguen separados.
4. La entrada puede venir desordenada y puede contener intervalos de un solo punto,
como `(5, 5)`.
5. La lista de entrada no se modifica.
6. Si algún intervalo tiene `inicio > fin`, lanza `ValueError`.
7. Una lista vacía devuelve `[]`.
8. Cada intervalo del resultado es una tupla `(inicio, fin)`.
Ejemplo:
```python
merge_intervals([(8, 10), (1, 3), (2, 6), (15, 18)])
# [(1, 6), (8, 10), (15, 18)]
```
A · replay:alfa
8/8 tests · resuelta · 3,4 s
def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
for start, end in intervals:
if start > end:
raise ValueError(f"Intervalo inválido: ({start}, {end})")
merged: list[tuple[int, int]] = []
for start, end in sorted(intervals):
if merged and start <= merged[-1][1]:
last_start, last_end = merged[-1]
merged[-1] = (last_start, max(last_end, end))
else:
merged.append((start, end))
return merged
Salida de pytest
........ [100%] 8 passed in 0.06s
B · replay:beta
8/8 tests · resuelta · 8,9 s
def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
"""Fusiona intervalos cerrados que se solapan o comparten un extremo."""
if not intervals:
return []
# Validamos antes de hacer nada para no devolver resultados parciales.
for interval in intervals:
start, end = interval
if start > end:
raise ValueError(f"El intervalo {interval!r} tiene inicio > fin")
# sorted() crea una lista nueva, así que la entrada no se modifica.
ordered = sorted(intervals, key=lambda item: item[0])
result: list[tuple[int, int]] = [tuple(ordered[0])]
for start, end in ordered[1:]:
current_start, current_end = result[-1]
if start <= current_end: # se solapan o se tocan
result[-1] = (current_start, max(current_end, end))
else:
result.append((start, end))
return result
Salida de pytest
........ [100%] 8 passed in 0.06s
Convertir una duración a segundosparse_durationA 15/16 B 16/16
# Convertir una duración a segundos
Escribe una función de Python con esta firma exacta:
```python
def parse_duration(text: str) -> int:
```
Recibe una duración escrita por una persona, como `"1h 30m 15s"`, y devuelve el total en
segundos (`5415` en ese ejemplo).
Formato válido:
1. La duración está formada por uno o más **componentes** `<entero><unidad>`, sin espacio
entre el número y la unidad.
2. Las unidades son `d` (días), `h` (horas), `m` (minutos) y `s` (segundos), en
minúsculas o mayúsculas.
3. Los componentes pueden ir juntos (`"1h30m"`) o separados por espacios, y se admiten
espacios al principio y al final.
4. Cada unidad aparece **como mucho una vez** y siempre en orden descendente:
`d`, luego `h`, luego `m`, luego `s`. Se pueden omitir unidades (`"2d 5s"` es válido).
5. El número es un entero sin signo; `"0s"` es válido y vale `0`.
Cualquier otra cosa debe lanzar `ValueError`, por ejemplo: cadena vacía o solo espacios,
un número sin unidad (`"10"`), una unidad desconocida (`"5x"`), una unidad repetida
(`"1h 2h"`), unidades fuera de orden (`"30m 1h"`), signos (`"-5m"`), decimales (`"1.5h"`)
o texto sobrante (`"1h y 5m"`).
A · replay:alfa
15/16 tests · 1 falla · 3,9 s
import re
_SECONDS = {"d": 86_400, "h": 3_600, "m": 60, "s": 1}
_FORMAT = re.compile(r"(?:\s*\d+[dhms])+\s*", re.IGNORECASE)
_COMPONENT = re.compile(r"(\d+)([dhms])", re.IGNORECASE)
def parse_duration(text: str) -> int:
if not _FORMAT.fullmatch(text):
raise ValueError(f"Duración no válida: {text!r}")
total = 0
seen: set[str] = set()
for number, unit in _COMPONENT.findall(text):
unit = unit.lower()
if unit in seen:
raise ValueError(f"Unidad repetida: {unit!r}")
seen.add(unit)
total += int(number) * _SECONDS[unit]
return total
Salida de pytest
...............F [100%]
================================== FAILURES ===================================
_______________ test_unidades_fuera_de_orden_lanzan_valueerror ________________
test_task.py:41: in test_unidades_fuera_de_orden_lanzan_valueerror
with pytest.raises(ValueError):
^^^^^^^^^^^^^^^^^^^^^^^^^
E Failed: DID NOT RAISE ValueError
=========================== short test summary info ===========================
FAILED test_task.py::test_unidades_fuera_de_orden_lanzan_valueerror - Failed:...
1 failed, 15 passed in 0.15sB · replay:beta
16/16 tests · resuelta · 11,5 s
import re
_UNITS = (("d", 86_400), ("h", 3_600), ("m", 60), ("s", 1))
_PATTERN = re.compile(
r"\s*"
r"(?:(?P<d>\d+)d)?\s*"
r"(?:(?P<h>\d+)h)?\s*"
r"(?:(?P<m>\d+)m)?\s*"
r"(?:(?P<s>\d+)s)?\s*",
re.IGNORECASE,
)
def parse_duration(text: str) -> int:
"""Convierte una duración como "1h 30m 15s" en segundos."""
match = _PATTERN.fullmatch(text)
if match is None:
raise ValueError(f"Duración no válida: {text!r}")
values = match.groupdict()
if all(value is None for value in values.values()):
raise ValueError("La duración está vacía")
return sum(int(values[unit]) * factor for unit, factor in _UNITS if values[unit] is not None)
Salida de pytest
................ [100%] 16 passed in 0.06s