modelduelInforme de duelo1 oct 2026 · 00:25 UTCv0.3.0

gamma vs alfa

3 tareas · 2 ejecuciones por tarea · límite de 20 s por ejecución de tests

Areplay:gamma
60/64
Breplay:alfa
62/64

tests superados

gana B por tests superados (62 frente a 60, con las mismas tareas resueltas) (suma de 2 ejecuciones)

2/3
Tareas resueltas
Tareas resueltas: A 2/3, B 2/3
todos los tests en todas las ejecuciones
2/3
4/6
Intentos resueltos
Intentos resueltos: A 4/6, B 4/6
ejecuciones con todos los tests en verde
4/6
60/64(peor)
Tests superados
Tests superados: A 60/64, B 62/64
suma de 2 ejecuciones
(mejor)62/64
7,4 s(mejor)tests: 2,5 s
Tiempo del modelo
Tiempo del modelo: A 7,4 s, B 20,2 s
menos es mejor · suma de 2 ejecuciones
(peor)20,2 stests: 2,5 s
3.480(mejor)entrada 2.628 · salida 852
Tokens
Tokens: A 3.480, B 4.208
menos es mejor
(peor)4.208entrada 2.802 · salida 1.406
0,0006 USD(mejor)
Coste estimado
Coste estimado: A 0,0006 USD, B 0,0034 USD
precios ficticios de demostración
(peor)0,0034 USD

Tests superados por tarea

Convertir un texto en slugfácil
A: 16 de 16 tests superados16/16
B: 16 de 16 tests superados16/16
Fusionar intervalosmedia
A: 12 de 16 tests superados12/16
B: 16 de 16 tests superados16/16
Convertir una duración a segundosdifícil
A: 32 de 32 tests superados32/32
B: 30 de 32 tests superados30/32

Tabla por tarea

Totales por tarea sumando 2 ejecuciones. El tiempo es el que tarda el modelo en responder.
TareaTestsTiempo del modeloTokens entrada / salidaCoste
ABABABAB
Convertir un texto en slugfácil16/1616/161,8 s5,7 s776 / 192824 / 3760,0002 USD0,0009 USD
Fusionar intervalosmedia12/1616/162,4 s6,7 s872 / 284936 / 4820,0002 USD0,0011 USD
Convertir una duración a segundosdifícil32/3230/323,2 s7,8 s980 / 3761.042 / 5480,0002 USD0,0013 USD

Código y salida de los tests

Convertir un texto en slugslugifyA 16/16 B 16/16
# 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:gamma
Ejecución 1 · 8/8 tests · resuelta · 0,9 s
import re
import unicodedata


def slugify(text: str, separator: str = "-") -> str:
    plain = "".join(
        char for char in unicodedata.normalize("NFD", text) if not unicodedata.combining(char)
    )
    return separator.join(re.findall(r"[a-z0-9]+", plain.lower()))
Salida de pytest
........                                                                 [100%]
8 passed in 0.06s
Ejecución 2 · 8/8 tests · resuelta · 0,9 s
import re
import unicodedata


def slugify(text: str, separator: str = "-") -> str:
    plain = "".join(
        char for char in unicodedata.normalize("NFD", text) if not unicodedata.combining(char)
    )
    return separator.join(re.findall(r"[a-z0-9]+", plain.lower()))
Salida de pytest
........                                                                 [100%]
8 passed in 0.06s
B · replay:alfa
Ejecución 1 · 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
Ejecución 2 · 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
Fusionar intervalosmerge_intervalsA 12/16 B 16/16
# 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:gamma
Ejecución 1 · 6/8 tests · 2 fallan · 1,2 s
def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
    merged: list[tuple[int, int]] = []
    for start, end in sorted(intervals):
        if start > end:
            raise ValueError(f"Intervalo no válido: {(start, end)}")
        if merged and start < merged[-1][1]:
            merged[-1] = (merged[-1][0], max(merged[-1][1], end))
        else:
            merged.append((start, end))
    return merged
Salida de pytest
...F.F..                                                                 [100%]
================================== FAILURES ===================================
______________ test_intervalos_que_comparten_extremo_se_fusionan ______________
test_task.py:19: in test_intervalos_que_comparten_extremo_se_fusionan
    assert merge_intervals([(3, 5), (1, 3), (5, 7)]) == [(1, 7)]
E   assert [(1, 3), (3, 5), (5, 7)] == [(1, 7)]
E     
E     At index 0 diff: (1, 3) != (1, 7)
E     Left contains 2 more items, first extra item: (3, 5)
E     Use -v to get more diff
______________________ test_intervalos_de_un_solo_punto _______________________
test_task.py:27: in test_intervalos_de_un_solo_punto
    assert merge_intervals([(5, 5), (1, 2), (2, 2), (7, 7)]) == [(1, 2), (5, 5), (7, 7)]
E   assert [(1, 2), (2, ...5, 5), (7, 7)] == [(1, 2), (5, 5), (7, 7)]
E     
E     At index 1 diff: (2, 2) != (5, 5)
E     Left contains one more item: (7, 7)
E     Use -v to get more diff
=========================== short test summary info ===========================
FAILED test_task.py::test_intervalos_que_comparten_extremo_se_fusionan - asse...
FAILED test_task.py::test_intervalos_de_un_solo_punto - assert [(1, 2), (2, ....
2 failed, 6 passed in 0.16s
Ejecución 2 · 6/8 tests · 2 fallan · 1,2 s
def merge_intervals(intervals: list[tuple[int, int]]) -> list[tuple[int, int]]:
    merged: list[tuple[int, int]] = []
    for start, end in sorted(intervals):
        if start > end:
            raise ValueError(f"Intervalo no válido: {(start, end)}")
        if merged and start < merged[-1][1]:
            merged[-1] = (merged[-1][0], max(merged[-1][1], end))
        else:
            merged.append((start, end))
    return merged
Salida de pytest
...F.F..                                                                 [100%]
================================== FAILURES ===================================
______________ test_intervalos_que_comparten_extremo_se_fusionan ______________
test_task.py:19: in test_intervalos_que_comparten_extremo_se_fusionan
    assert merge_intervals([(3, 5), (1, 3), (5, 7)]) == [(1, 7)]
E   assert [(1, 3), (3, 5), (5, 7)] == [(1, 7)]
E     
E     At index 0 diff: (1, 3) != (1, 7)
E     Left contains 2 more items, first extra item: (3, 5)
E     Use -v to get more diff
______________________ test_intervalos_de_un_solo_punto _______________________
test_task.py:27: in test_intervalos_de_un_solo_punto
    assert merge_intervals([(5, 5), (1, 2), (2, 2), (7, 7)]) == [(1, 2), (5, 5), (7, 7)]
E   assert [(1, 2), (2, ...5, 5), (7, 7)] == [(1, 2), (5, 5), (7, 7)]
E     
E     At index 1 diff: (2, 2) != (5, 5)
E     Left contains one more item: (7, 7)
E     Use -v to get more diff
=========================== short test summary info ===========================
FAILED test_task.py::test_intervalos_que_comparten_extremo_se_fusionan - asse...
FAILED test_task.py::test_intervalos_de_un_solo_punto - assert [(1, 2), (2, ....
2 failed, 6 passed in 0.15s
B · replay:alfa
Ejecución 1 · 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
Ejecución 2 · 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.05s
Convertir una duración a segundosparse_durationA 32/32 B 30/32
# 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:gamma
Ejecución 1 · 16/16 tests · resuelta · 1,6 s
import re

_PATTERN = re.compile(
    r"\s*(?:(\d+)d)?(?:\s*(\d+)h)?(?:\s*(\d+)m)?(?:\s*(\d+)s)?\s*", re.IGNORECASE
)


def parse_duration(text: str) -> int:
    match = _PATTERN.fullmatch(text)
    if match is None or all(group is None for group in match.groups()):
        raise ValueError(f"Duración no válida: {text!r}")
    days, hours, minutes, seconds = (int(group or 0) for group in match.groups())
    return ((days * 24 + hours) * 60 + minutes) * 60 + seconds
Salida de pytest
................                                                         [100%]
16 passed in 0.06s
Ejecución 2 · 16/16 tests · resuelta · 1,6 s
import re

_PATTERN = re.compile(
    r"\s*(?:(\d+)d)?(?:\s*(\d+)h)?(?:\s*(\d+)m)?(?:\s*(\d+)s)?\s*", re.IGNORECASE
)


def parse_duration(text: str) -> int:
    match = _PATTERN.fullmatch(text)
    if match is None or all(group is None for group in match.groups()):
        raise ValueError(f"Duración no válida: {text!r}")
    days, hours, minutes, seconds = (int(group or 0) for group in match.groups())
    return ((days * 24 + hours) * 60 + minutes) * 60 + seconds
Salida de pytest
................                                                         [100%]
16 passed in 0.06s
B · replay:alfa
Ejecución 1 · 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.15s
Ejecución 2 · 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.15s