383 lines
18 KiB
Markdown
383 lines
18 KiB
Markdown
# 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.
|
||
|
||
1. **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.
|
||
2. **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.
|
||
3. **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.
|
||
4. **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.
|
||
5. **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.
|
||
6. **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.
|
||
|
||
````markdown
|
||
### 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.md` mantiene 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:
|
||
|
||
1. Estructura de un programa, `main`, `#include`, compilación.
|
||
2. Variables, tipos primitivos, constantes.
|
||
3. Operadores aritméticos, lógicos y relacionales, precedencia.
|
||
4. Condicionales: `if`, `else`, `switch`.
|
||
5. Bucles: `for`, `while`, `do-while`.
|
||
6. Funciones: parámetros, retorno, alcance, prototipos.
|
||
7. Arreglos y cadenas de caracteres.
|
||
8. Punteros y aritmética de punteros.
|
||
9. `struct`, `typedef`, `union`, `enum`.
|
||
10. Memoria dinámica: `malloc`, `free`, manejo de errores.
|
||
11. Archivos: lectura y escritura con `stdio.h`.
|
||
12. 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_case` o `camelCase` según se elija por capítulo, pero **consistente** dentro del mismo.
|
||
- **Números de ejercicio**: `N.M`, donde `N` es el número del capítulo y `M` el 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. `EOF` vs `'\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.js` los resalte (sección 10).
|
||
- **Escenario de prueba** en cada ejercicio: un bloque `text` con 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-avisar` conceptos 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:
|
||
|
||
1. Elegir el capítulo (archivo) y el número de ejercicio.
|
||
2. Declarar los **conceptos** objetivo.
|
||
3. Redactar el **contexto real** y aclarar la simplificación.
|
||
4. Escribir la **consigna**, entrada y salida de forma inequívoca.
|
||
5. Agregar un **ejemplo** de entrada/salida si aporta claridad.
|
||
6. Sugerir **identificadores en inglés**.
|
||
7. Añadir **criterios de aceptación** verificables.
|
||
8. Revisar que **no haya código de solución**.
|
||
9. Verificar idioma (español), ortografía y consistencia de estilo.
|
||
10. Actualizar el índice del `README.md` si 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`** (herramienta `md-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.js` aplica **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.css` ya lo aplica
|
||
(`break-after: avoid` en los títulos y en el primer párrafo que los sigue, más `orphans`/`widows: 3`);
|
||
no romper esas reglas.
|
||
- `printBackground` debe 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)
|
||
|
||
1. **Escribir con `write` solo 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.
|
||
|
||
2. **Usar `edit` para ir agregando las secciones restantes**. Cada llamada a `edit` reemplaza
|
||
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
|
||
```
|
||
|
||
3. **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 `\n` en 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,
|
||
> usar `write` para la primera parte y `edit` sucesivo 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
|
||
|
||
```python
|
||
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:
|
||
|
||
1. **Borrar el archivo corrupto.** No intentes repararlo con `edit` — si ya tiene entidades HTML
|
||
mezcladas con bytes dobles, la reparación parcial deja residuos.
|
||
|
||
2. **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 uses `write` del agente para contenido > 600 líneas.
|
||
|
||
```python
|
||
# 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
|
||
```
|
||
|
||
3. **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.
|
||
|
||
4. **Verificar** que no queden caracteres corruptos:
|
||
```python
|
||
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 en `write` puede
|
||
> corromper la codificación. La forma segura es: script Python que construya el Unicode interno y
|
||
> lo escriba con `encoding="utf-8"`.
|