18 KiB
AGENTS.md
Guía de trabajo para agentes de IA (y humanos) que colaboran en este repositorio.
1. Qué es este proyecto
Este repositorio es un libro de ejercicios de programación en lenguaje C.
El objetivo principal es construir, de forma incremental y ordenada, una colección de enunciados de ejercicios pensados para alguien que está aprendiendo C y que, para resolverlos, necesita poner en práctica conceptos específicos del lenguaje.
Cada enunciado:
- Presenta un problema de la vida real como contexto.
- Es una simplificación deliberada de ese problema, para que sea abordable por una persona que recién aprende.
- Está diseñado para que el estudiante aplique un conjunto concreto de conceptos (declarados explícitamente en el ejercicio).
- Nunca incluye la solución en código. El libro provee el problema, no la respuesta.
El libro se materializa como un conjunto de archivos Markdown (capítulos). El repositorio no contiene código C de solución; contiene enunciados.
2. Reglas no negociables
Estas reglas tienen prioridad sobre cualquier otra instrucción. Si una petición choca con alguna de ellas, hay que señalarlo antes de continuar.
- NUNCA generar el código de solución. No se escribe, insinúa ni deja entrever la implementación completa del enunciado. Se permiten, como máximo, fragmentos ilustrativos de entrada/salida esperada (texto por consola), no de lógica del programa.
- Los enunciados siempre en español. El texto dirigido al estudiante (título, contexto, consigna, ejemplos, aclaraciones) va en español, con ortografía y redacción cuidadas.
- Contexto real, complejidad simplificada. Todo enunciado parte de una situación del mundo real, pero recortada a lo esencial para que sea resoluble con los conceptos objetivo. Nunca convertir el ejercicio en un proyecto de ingeniería.
- Conceptos explícitos. Cada ejercicio declara qué conceptos de C debe ejercitar. La consigna tiene que poder resolverse usando esos conceptos y no otros innecesarios.
- Claridad absoluta. Un enunciado ambiguo es un enunciado roto. Debe quedar claro qué entra, qué sale y qué se espera. Se recomienda incluir un ejemplo.
- Identificadores pueden sugerirse en inglés. Los nombres de variables, funciones y tipos sugeridos van en inglés (idioma de facto del código). No es obligatorio imponerlos, pero sí la convención del repositorio.
3. Anatomía de un ejercicio
Todo ejercicio sigue esta plantilla. Los campos marcados como opcionales pueden omitirse cuando aportan poco.
### Ejercicio N.M — [Título corto en español]
**Conceptos** · `concepto-1`, `concepto-2`, `concepto-3`
**Contexto** · Situación real en 1–3 oraciones (qué problema del mundo real motiva el ejercicio).
Aclarar de forma explícita que es una simplificación.
**Consigna** · Qué hay que lograr. Enumerable si tiene varios pasos. Sin ambigüedad.
**Entrada** · Qué recibe el programa (formato, tipo y rango esperado).
**Salida** · Qué debe producir, con el formato esperado.
**Ejemplo** *(opcional)* ·
```
Entrada: ...
Salida: ...
```
**Identificadores sugeridos** *(opcional)* · `average`, `studentList`, `MAX_ITEMS`
(En inglés. Se ofrecen como guía, no como obligación.)
**Restricciones / pistas** *(opcional)* · Qué construcciones usar,
qué evitar, casos borde a considerar. NUNCA la lógica de la solución.
**Criterios de aceptación** · Lista verificable de qué debe cumplir una solución correcta.
Regla de oro del ejemplo
El bloque de Ejemplo ilustra entrada y salida (o un caso de uso textual). Jamás muestra cómo se calcula internamente. Está permitido describir con palabras el resultado; está prohibido mostrar el algoritmo.
4. Estructura del libro (archivos)
El libro vive bajo un directorio raíz de contenido. Se propone:
c-programming/
├─ AGENTS.md # este archivo (reglas del proyecto)
├─ README.md # presentación del libro e índice general
├─ book/ # FUENTE: capítulos en Markdown
│ ├─ 00-introduccion.md
│ ├─ 01-conceptos-basicos.md
│ ├─ 02-variables-y-tipos.md
│ ├─ 03-operadores-y-expresiones.md
│ ├─ 04-estructuras-de-control.md
│ ├─ 05-funciones.md
│ ├─ 06-arreglos-y-cadenas.md
│ ├─ 07-punteros.md
│ ├─ 08-structs-y-uniones.md
│ ├─ 09-memoria-dinamica.md
│ ├─ 10-archivos.md
│ ├─ 11-estructuras-de-datos.md
│ └─ 12-arenas.md
├─ dist/ # SALIDA: un PDF por cada capítulo trabajado
│ └─ 12-arenas.pdf
├─ assets/
│ └─ pdf/
│ └─ print.css # hoja de estilo canónica de los PDF
└─ .opencode/
└─ skills/
└─ book-pdf/SKILL.md # procedimiento para generar los PDF
- Un archivo por tema/concepto principal.
- El
README.mdmantiene el índice y el enlace a cada capítulo. - Nombres de archivo en kebab-case y con prefijo numérico para conservar el orden.
- El prefijo numérico del archivo se usa también para numerar los ejercicios (
### Ejercicio 04.2).
5. Progresión de conceptos
Los ejercicios deben seguir una dificultad creciente. Orden de referencia de los conceptos:
- Estructura de un programa,
main,#include, compilación. - Variables, tipos primitivos, constantes.
- Operadores aritméticos, lógicos y relacionales, precedencia.
- Condicionales:
if,else,switch. - Bucles:
for,while,do-while. - Funciones: parámetros, retorno, alcance, prototipos.
- Arreglos y cadenas de caracteres.
- Punteros y aritmética de punteros.
struct,typedef,union,enum.- Memoria dinámica:
malloc,free, manejo de errores. - Archivos: lectura y escritura con
stdio.h. - Estructuras de datos simples (listas, pilas, colas) y algoritmos básicos de ordenamiento/búsqueda.
Un ejercicio puede combinar conceptos de niveles anteriores, pero no debe exigir conceptos de niveles posteriores sin declararlos como objetivo.
6. Convenciones de redacción
- Tono: docente, claro y motivador. Tratar al lector de "vos" o de forma neutra, de manera consistente dentro de un capítulo.
- Idioma del texto: español. Términos técnicos del lenguaje pueden quedar en inglés cuando sea lo natural (
array,pointer,struct), pero preferir el término en español con el inglés entre paréntesis la primera vez. - Identificadores sugeridos: en inglés,
lowercase_snake_caseocamelCasesegún se elija por capítulo, pero consistente dentro del mismo. - Números de ejercicio:
N.M, dondeNes el número del capítulo yMel del ejercicio. - Extensión: enunciados concisos. Si un ejercicio crece demasiado, dividirlo.
- Un problema real por ejercicio, con su simplificación aclarada.
7. Estilo didáctico al explicar conceptos ("para tontos")
Los capítulos no solo enuncian ejercicios: también explican los conceptos que el lector necesita.
Esa explicación sigue un estilo llano, concreto y progresivo —el mismo que se usó en
book/14-entrada-salida-estandar.md—, pensado para que el lector entienda de verdad y no solo
lea. Estas reglas aplican a todo capítulo que explique conceptos.
Analogía cotidiana primero. Todo concepto arranca con una comparación del mundo real que el lector
ya domina; recién después aparece el término técnico. Ejemplos del capítulo 14: las tres tuberías
(stdin/stdout/stderr) son "una fábrica"; getchar es "sacar caramelos de a uno"; el búfer es
"el villano de la película". La analogía se sostiene durante toda la explicación; no se abandona a la
primera frase.
Títulos con gancho, no etiquetas. En vez de "Introducción a getchar", el título promete algo
concreto: "getchar: el caramelito de a uno". Lo mismo vale para las subsecciones.
Explicar el POR QUÉ, no solo el qué. Cada regla llega con su motivo. No alcanza con decir "guardá
en int, no en char": hay que explicar por qué (tiene que representar los 256 bytes posibles y
un valor extra, EOF).
Voz cercana y frases cortas. Se trata al lector de "vos" (ver sección 6). Párrafos breves, en presente, sin rodeos. Se permite —y se espera— marcar la idea clave con negrita y con MAYÚSCULAS ocasionales.
Cómo estructurar un capítulo largo:
- Dividirlo en partes con un índice al inicio. La tabla lleva, por cada parte, "la idea en una frase".
- Después de cada concepto, un bloque de práctica con uno o más ejercicios cortos, antes de avanzar al siguiente concepto.
- Cerrar con un apartado tipo "lo que te llevás": las ideas clave en pocos bullets, y el puente al capítulo siguiente.
Recursos que se repiten:
- Apodos para las trampas clásicas: "la trampa N.º 1", "el bug más famoso del principiante", "el villano de la película".
- Blockquotes (
>) para notas, advertencias y "momentos ajá": "Momento ajá", "Ojo con esto", "Para guardar", "Truco de laboratorio". En el PDF todas se dibujan con fondo de color (sección 10). - Tablas comparativas para contrastar opciones o señales parecidas (p. ej.
EOFvs'\0'vs'\n'). - Diagramas ASCII para mostrar lo visual: flujos conectados, los dos búferes en serie.
- Código ilustrativo comentado en lenguaje llano: cada línea que introduce algo nuevo lleva un
comentario claro y de varias líneas que dice exactamente qué hace. Los bloques declaran su
lenguaje en el fence para que
highlight.jslos resalte (sección 10). - Escenario de prueba en cada ejercicio: un bloque
textcon la entrada y la salida esperada, para que el lector pueda comprobar su solución.
El límite que NO se cruza. Este estilo permite código ilustrativo para explicar un concepto (una llamada a una función, un esqueleto mínimo, un patrón genérico), pero nunca la solución de un ejercicio. Las reglas no negociables (sección 2) siguen mandando: se entrega el problema; la lógica que resuelve la consigna la escribe el lector. El código de ejemplo sirve para entender una herramienta, jamás para resolver un enunciado del libro.
8. Lo que está prohibido
- Escribir la solución, total o parcial, del enunciado (incluida la lógica en pseudocódigo).
- Convertir el ejercicio en un proyecto grande, con build system, múltiples archivos o dependencias externas.
- Usar bibliotecas que no sean la librería estándar de C, salvo que sea el tema explícito del capítulo.
- Dejar conceptos de la consigna sin declarar o usar-
sin-avisarconceptos fuera del objetivo. - Enunciados ambiguos, incompletos o sin criterios de aceptación.
- Mezclar idiomas dentro de un mismo enunciado (salvo la excepción de identificadores sugeridos).
9. Flujo para agregar contenido
Checklist al crear o editar un ejercicio:
- Elegir el capítulo (archivo) y el número de ejercicio.
- Declarar los conceptos objetivo.
- Redactar el contexto real y aclarar la simplificación.
- Escribir la consigna, entrada y salida de forma inequívoca.
- Agregar un ejemplo de entrada/salida si aporta claridad.
- Sugerir identificadores en inglés.
- Añadir criterios de aceptación verificables.
- Revisar que no haya código de solución.
- Verificar idioma (español), ortografía y consistencia de estilo.
- Actualizar el índice del
README.mdsi se agregó un capítulo.
Recordatorio permanente: se entrega el problema, nunca la respuesta.
10. Generación de PDF (dist/)
Cada capítulo trabajado se publica como PDF en dist/, con el mismo nombre que su
Markdown: book/12-arenas.md → dist/12-arenas.pdf.
- Los PDF se generan con el MCP
md-to-pdf(herramientamd-to-pdf_markdown_to_pdf). No instalar pandoc/LaTeX ni armar un pipeline propio: ya existe. - El diseño es único y compartido: vive en
assets/pdf/print.css. No se estiliza capítulo por capítulo. - El procedimiento completo y los parámetros fijos de generación están en la skill del
proyecto:
.opencode/skills/book-pdf/SKILL.md. Hay que seguirla siempre para que todos los PDF tengan el mismo diseño.
Reglas de diseño (resumen):
- Fuente de código: monoespaciada, distinta de la fuente del cuerpo (serif). Todo bloque de
código declara su lenguaje en el fence (
```c,```bash,```text); asíhighlight.jsaplica resaltado de sintaxis. Un bloque sin lenguaje no se resalta (las trazas de consola van como```text). - Notas / advertencias: se escriben como blockquote (
>). En el PDF todas se dibujan igual, con un fondo de color que resalta del blanco de la hoja. - Títulos sin huérfanos: un título (
h1–h4) nunca puede quedar solo al pie de página: siempre tiene que llevar al menos tres líneas de contenido debajo, en la misma hoja. Si no caben, se fuerza un salto de página antes del título.print.cssya lo aplica (break-after: avoiden los títulos y en el primer párrafo que los sigue, másorphans/widows: 3); no romper esas reglas. printBackgrounddebe estar activo (si no, no se pintan los fondos de código y notas).- Encabezado, pie de página y numeración de páginas fijos.
Si se modifica
assets/pdf/print.css, hay que regenerar todos los capítulos para que ningún PDF quede con un diseño distinto.
Protocolo de escritura de archivos extensos
Cuando el agente (humano o IA) necesita escribir un archivo Markdown de contenido grande
(≥ 600 líneas, como los capítulos del libro), no usar una única llamada a write con todo el contenido.
Los herramientas de escritura pasan el contenido dentro de un string JSON y los caracteres especiales
(tres backticks ```, $, {, }, \, comillas) pueden romper el parseo.
Enfoque correcto (confirmado en producción)
-
Escribir con
writesolo los primeros ~500-600 líneas, hasta la primera sección completa (hasta un---o final de una parte). El archivo se crea correctamente porque el contenido es manejable para el parser JSON. -
Usar
editpara ir agregando las secciones restantes. Cada llamada aeditreemplaza una cadena corta (oldString) por otra más larga (newString). La cadena a buscar es siempre muy breve (ej: el último texto del archivo actual) y la cadena de reemplazo incluye la nueva sección.oldString: "- [ ] Termina limpiamente al escribir \"exit\"." ← solo la última línea existente newString: "- [ ] Termina limpiamente al escribir \"exit\".\n\n---\n# PARTE 5 — ..." ← misma + nueva sección -
Verificar el conteo de líneas con un bash/PowerShell simple después de cada edit grande, para confirmar que la sección se incorporó correctamente:
Get-Content book\22-file-descriptors.md | Measure-Object -Line
Por qué fallaba el enfoque anterior
Un archivo de capítulo tiene ~1200-1500 líneas con:
- Bloques de código C con backticks triples (
```c) que el parser JSON confunde - Strings con comillas escapadas (
\") que colisionan con las del string JSON - Caracteres especiales como
$en ejemplos de shell o\nen strings C
Con chunks de ≤ 600 líneas, el contenido cabe en un string JSON sin romper el parseo.
Regla práctica
Nunca escribir más de 600 líneas de una sola vez con
write. Si el contenido total es mayor, usarwritepara la primera parte yeditsucesivo para el resto.
11. Corrección de caracteres malformados (mojibake)
Los capítulos en español pueden acumular codificación doble: bytes UTF-8 almacenados como si
fuera Latin-1/CP1252, lo que genera mojibake visible como ú, Práctica o caracteres
\xef\xbf\xbd (replacement char) en vez de "práctica". Esto pasa cuando el parser JSON del tool
de escritura corrompe los bytes multibyte.
Cómo detectar
import os
path = r"C:\Users\horacio\Desarrollo\c-programming\book\TU-CAPITULO.md"
with open(path, "rb") as f:
raw = f.read()
# Si hay bytes no ASCII pero el archivo se decodifica como UTF-8 bien formado,
# puede haber entidades HTML o caracteres dobles codificados.
try:
text = raw.decode("utf-8")
except UnicodeDecodeError:
print("NO es UTF-8 — hay corrupción de encoding")
# Buscar replacement char U+FFFD (EF BF BD)
if b"\xef\xbf\xbd" in raw:
print("Encontrado replacement char — el archivo tiene mojibake")
# Buscar entidades HTML sueltas (ú, é, ñ)
for ent in [b"ú", b"é", b"ñ", b"á", b"ó", b"ú"]:
if ent in raw:
print(f"Entidad HTML detectada: {ent}")
Cómo corregir (reconstruir UTF-8 limpio)
Si el archivo está corrupto, borrar y reconstruir desde un script Python que escriba en UTF-8 nativo. El pattern probado en producción es:
-
Borrar el archivo corrupto. No intentes repararlo con
edit— si ya tiene entidades HTML mezcladas con bytes dobles, la reparación parcial deja residuos. -
Escribir con un script Python que use
encoding='utf-8'. El script construye el contenido como strings de Python (que son Unicode internos) y lo escribe como bytes UTF-8 limpios. No useswritedel agente para contenido > 600 líneas.# rebuild_chapter.py chapter_file = r"C:\Users\horacio\Desarrollo\c-programming\book\25-drm-kms.md" with open(chapter_file, "w", encoding="utf-8") as f: f.write("# Capítulo 25 — DRM/KMS\n") # texto en Unicode → se escribe como UTF-8 limpio -
Si el contenido es largo, dividí en chunks de ~250-300 líneas y uso
open(..., 'a')para append cada chunk al archivo ya creado. -
Verificar que no queden caracteres corruptos:
raw = open(path, "rb").read() text = raw.decode("utf-8", errors="replace") assert "\ufffd" not in text # sin replacement chars for ent in [b"ú", b"é", b"ñ"]: assert ent not in raw # sin entidades HTML sueltas
Regla de prevención
Nunca pasar texto con acentos/ñ directamente en un string JSON
write. Si el contenido del capítulo tiene caracteres UTF-8 multibyte, escribirlo como un bloque grande enwritepuede corromper la codificación. La forma segura es: script Python que construya el Unicode interno y lo escriba conencoding="utf-8".