modelduelInforme de liga1 oct 2026 · 20:26 UTCv0.7.0

alfa vs beta vs gamma

3 contendientes · 3 tareas · 1 ejecución por tarea · límite de 20 s por ejecución de tests

Primero: Aalfa · 2/3 tareas · 31/32 tests

Clasificación

Orden: tareas resueltas, después tests superados y después coste (menos es mejor). Los empates comparten posición. El tiempo es el que tarda el modelo en responder.
Pos.ContendienteTareasTestsTiempoTokens ent. / sal.Coste
1(mejor)Aalfareplay:alfa2/331/32A: 31 de 32 tests superados10,1 s1.401 / 7030,0017 USD
2Cgammareplay:gamma2/330/32C: 30 de 32 tests superados3,7 s1.314 / 4260,0003 USD
3Bbetareplay:beta2/330/32B: 30 de 32 tests superados28,1 s1.373 / 2.1320,0248 USD

Comparativa

Tareas resueltas

todos los tests en todas las ejecuciones

AalfaTareas resueltas: A alfa, 2/32/3
BbetaTareas resueltas: B beta, 2/32/3
CgammaTareas resueltas: C gamma, 2/32/3

Tests superados

AalfaTests superados: A alfa, 31/3231/32(mejor)
BbetaTests superados: B beta, 30/3230/32
CgammaTests superados: C gamma, 30/3230/32

Tiempo del modelo

menos es mejor

AalfaTiempo del modelo: A alfa, 10,1 s10,1 stests: 0,9 s
BbetaTiempo del modelo: B beta, 28,1 s28,1 stests: 0,9 s
CgammaTiempo del modelo: C gamma, 3,7 s3,7 s(mejor)tests: 0,9 s

Tokens

menos es mejor

AalfaTokens: A alfa, 2.1042.104entrada 1.401 · salida 703
BbetaTokens: B beta, 3.5053.505entrada 1.373 · salida 2.132
CgammaTokens: C gamma, 1.7401.740(mejor)entrada 1.314 · salida 426

Coste estimado

precios ficticios de demostración

AalfaCoste estimado: A alfa, 0,0017 USD0,0017 USD
BbetaCoste estimado: B beta, 0,0248 USD0,0248 USD
CgammaCoste estimado: C gamma, 0,0003 USD0,0003 USD(mejor)

Matriz por tarea

Tests superados por tarea.
TareaAalfaBbetaCgamma
Convertir un texto en slugfácil8/8resueltaA: 8 de 8 tests superados en Convertir un texto en slug6/8no resueltaB: 6 de 8 tests superados en Convertir un texto en slug8/8resueltaC: 8 de 8 tests superados en Convertir un texto en slug
Fusionar intervalosmedia8/8resueltaA: 8 de 8 tests superados en Fusionar intervalos8/8resueltaB: 8 de 8 tests superados en Fusionar intervalos6/8no resueltaC: 6 de 8 tests superados en Fusionar intervalos
Convertir una duración a segundosdifícil15/16no resueltaA: 15 de 16 tests superados en Convertir una duración a segundos16/16resueltaB: 16 de 16 tests superados en Convertir una duración a segundos16/16resueltaC: 16 de 16 tests superados en Convertir una duración a segundos
Total31/322/3 tareas30/322/3 tareas30/322/3 tareas

Código y salida de los tests

Convertir un texto en slugslugifyA 8/8 B 6/8 C 8/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.02s
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 'python-312-en-2026' == 'python-3-12-en-2026'
  
  - python-3-12-en-2026
  ?         -
  + python-312-en-2026
FAILED test_task.py::test_barra_y_guion_bajo_separan - AssertionError: assert 'entradasalida' == 'entrada-salida'
  
  - entrada-salida
  ?        -
  + entradasalida
2 failed, 6 passed in 0.04s
C · replay:gamma
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.02s
Fusionar intervalosmerge_intervalsA 8/8 B 8/8 C 6/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.02s
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.02s
C · replay:gamma
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     
E     Full diff:
E       [
E           (
E               1,
E     +         3,
E     +     ),
E     +     (
E     +         3,
E     +         5,
E     +     ),
E     +     (
E     +         5,
E               7,
E           ),
E       ]
_______________________ 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     
E     Full diff:
E       [
E           (
E               1,
E     +         2,
E     +     ),
E     +     (
E     +         2,
E               2,
E           ),
E           (
E               5,
E               5,
E           ),
E           (
E               7,
E               7,
E           ),
E       ]
=========================== short test summary info ============================
FAILED test_task.py::test_intervalos_que_comparten_extremo_se_fusionan - assert [(1, 3), (3, 5), (5, 7)] == [(1, 7)]
  
  At index 0 diff: (1, 3) != (1, 7)
  Left contains 2 more items, first extra item: (3, 5)
  
  Full diff:
    [
        (
            1,
  +         3,
  +     ),
  +     (
  +         3,
  +         5,
  +     ),
  +     (
  +         5,
            7,
        ),
    ]
FAILED test_task.py::test_intervalos_de_un_solo_punto - assert [(1, 2), (2, ...5, 5), (7, 7)] == [(1, 2), (5, 5), (7, 7)]
  
  At index 1 diff: (2, 2) != (5, 5)
  Left contains one more item: (7, 7)
  
  Full diff:
    [
        (
            1,
  +         2,
  +     ),
  +     (
  +         2,
            2,
        ),
        (
            5,
            5,
        ),
        (
            7,
            7,
        ),
    ]
2 failed, 6 passed in 0.04s
Convertir una duración a segundosparse_durationA 15/16 B 16/16 C 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: DID NOT RAISE ValueError
1 failed, 15 passed in 0.04s
B · 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.03s
C · replay:gamma
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.03s