feat: Demo-Examples (Python/Rust/Go/C) mit Protokoll-Templates und Restore-Skript

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-05-29 19:06:36 +02:00
commit 64c2b7f0fd
21 changed files with 614 additions and 0 deletions

7
.gitignore vendored
View file

@ -1,3 +1,10 @@
*.bak
node_modules/
.DS_Store
# Build-Artefakte (Examples)
examples/rust-wordcount/target/
examples/**/__pycache__/
examples/**/.pytest_cache/
examples/**/Cargo.lock
examples/**/ll_demo

74
examples/README.md Normal file
View file

@ -0,0 +1,74 @@
# pi-coder Beispielprojekte
Vier kleine, eigenständige Projekte als Demonstrationsgrundlage für die pi-coder-Features.
Jedes Projekt startet bewusst unvollständig — genau der Ausgangspunkt, für den pi-coder gebaut ist.
## Übersicht
| Verzeichnis | Sprache | Demonstriert |
|---|---|---|
| `python-calculator/` | Python | `/optimize` mit `--test-cmd pytest` |
| `rust-wordcount/` | Rust | `/optimize` mit `--test-cmd "cargo test"` + `/version` |
| `go-fibonacci/` | Go | `/optimize --interactive` + `/continue` + `/shipit` |
| `c-linkedlist/` | C | `/quick_check` + `/fix` + `/patch` |
## Demo-Workflow
### Schritt 1 — Vorbereitung: Sub-Repos anlegen
Jedes Example braucht ein eigenes git-Repo, damit pi-coder commit-basierte
Features nutzen kann (Loop-Erkennung, Diff-Anzeige, `/version`):
```bash
for dir in python-calculator rust-wordcount go-fibonacci c-linkedlist; do
cd examples/$dir
git init && git add -A && git commit -m "feat: initial $dir"
cd ../..
done
```
Für `/version` im rust-wordcount-Beispiel zusätzlich:
```bash
cd examples/rust-wordcount && git tag v0.1.0
```
### Schritt 2 — Demo ausführen
In pi das jeweilige Unterverzeichnis als Arbeitsverzeichnis öffnen.
Die genauen Befehle stehen im README.md des jeweiligen Examples.
Zeitmessung: Systemuhr notieren oder Terminal-Kommando `time` nutzen.
### Schritt 3 — Protokoll ausfüllen
Jedes Example enthält eine `PROTOKOLL.md`.
Startzeit, Endzeit, Rundenanzahl und Endergebnis eintragen.
### Schritt 4 — Ausgangszustand wiederherstellen
```bash
bash examples/restore-all.sh
```
Das Skript löscht Sub-Repos, restauriert alle Quelldateien aus dem Haupt-Repo
und bereinigt Build-Artefakte (`target/`, `__pycache__` etc.).
---
## Empfohlene Demo-Reihenfolge
| # | Beispiel | Geschätzte Dauer | Highlights |
|---|---|---|---|
| 1 | `python-calculator` | ~510 min | Einstieg, Test-Loop |
| 2 | `c-linkedlist` | ~5 min | `/quick_check` + `/fix`, kein Loop |
| 3 | `rust-wordcount` | ~1015 min | Loop + `/version` |
| 4 | `go-fibonacci` | ~1520 min | `--interactive` + `/shipit` |
---
## Weitere Details
[python-calculator](python-calculator/README.md) ·
[rust-wordcount](rust-wordcount/README.md) ·
[go-fibonacci](go-fibonacci/README.md) ·
[c-linkedlist](c-linkedlist/README.md)

View file

@ -0,0 +1,35 @@
# Demo-Protokoll: c-linkedlist
## Lauf 1
**Datum:**
**Befehl /quick_check:**
```
/quick_check "Gibt es Speicherlecks oder sonstige Probleme in diesem C-Projekt?"
```
**Startzeit:**
**Endzeit:**
**Dauer (min):**
**Ergebnis:** OK / PROBLEM (Kurzbeschreibung):
**Befehl /fix:**
```
/fix "Implementiere list_free() korrekt, sodass valgrind --leak-check=full sauber ist."
```
**Startzeit:**
**Endzeit:**
**Dauer (min):**
**Ergebnis:** erledigt / fehlgeschlagen
**Befehl /patch (optional):**
```
/patch "Ergänze list_search(head, value) in Header und Implementierung.
Gibt den ersten Node* mit dem gesuchten Wert zurück, oder NULL."
```
**Startzeit:**
**Endzeit:**
**Dauer (min):**
**Besonderheiten / Beobachtungen:**
---

View file

@ -0,0 +1,48 @@
# C Linked List
Vollständige einfach-verkettete Liste — bis auf `list_free()`, das als leerer Stub vorliegt.
Jeder Programmlauf leckt den gesamten Listen-Speicher.
## Aktueller Stand
```
linked_list.h Interface: node_new, list_prepend, list_append, list_print, list_free, list_length
linked_list.c Alles implementiert — außer list_free() (Stub, tut nichts)
main.c Baut Liste 15, gibt sie aus, ruft list_free() auf (ohne Wirkung)
```
## Demo 1: `/quick_check` als Diagnose
```
/quick_check "Gibt es Speicherlecks oder sonstige Probleme in diesem C-Projekt?"
```
Der Judge analysiert den Code und identifiziert das leere `list_free()` als Speicherleck-Quelle.
## Demo 2: `/fix` für gezieltes Nacharbeiten
```
/fix "Implementiere list_free() korrekt, sodass valgrind --leak-check=full sauber ist."
```
Coder implementiert die Funktion, committet. Kein vollständiger Judge-Loop —
ideal für kleine, klar abgegrenzte Fixes.
## Demo 3: `/patch` für Minimal-Erweiterungen
```
/patch "Ergänze list_search(head, value) in Header und Implementierung.
Gibt den ersten Node* mit dem gesuchten Wert zurück, oder NULL."
```
pi-coder wendet einen unified diff an (`apply_patch`-Tool), ohne den vollständigen Loop.
## Manueller Build
```bash
gcc -Wall -Wextra -o ll_demo linked_list.c main.c
./ll_demo
# Mit Leak-Check:
valgrind --leak-check=full ./ll_demo
```

View file

@ -0,0 +1,43 @@
#include <stdlib.h>
#include <stdio.h>
#include "linked_list.h"
Node *node_new(int value) {
Node *n = malloc(sizeof(Node));
n->value = value;
n->next = NULL;
return n;
}
Node *list_prepend(Node *head, int value) {
Node *n = node_new(value);
n->next = head;
return n;
}
Node *list_append(Node *head, int value) {
Node *n = node_new(value);
if (!head) return n;
Node *cur = head;
while (cur->next) cur = cur->next;
cur->next = n;
return head;
}
void list_print(const Node *head) {
for (const Node *cur = head; cur; cur = cur->next)
printf("%d ", cur->value);
printf("\n");
}
/* BUG: Speicher wird nicht freigegeben — valgrind meldet Leaks. */
void list_free(Node *head) {
(void)head; /* TODO: implementieren */
}
int list_length(const Node *head) {
int len = 0;
for (const Node *cur = head; cur; cur = cur->next)
len++;
return len;
}

View file

@ -0,0 +1,16 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
typedef struct Node {
int value;
struct Node *next;
} Node;
Node *node_new(int value);
Node *list_prepend(Node *head, int value);
Node *list_append(Node *head, int value);
void list_print(const Node *head);
void list_free(Node *head);
int list_length(const Node *head);
#endif

View file

@ -0,0 +1,16 @@
#include <stdio.h>
#include "linked_list.h"
int main(void) {
Node *list = NULL;
for (int i = 1; i <= 5; i++)
list = list_append(list, i);
printf("Liste: ");
list_print(list);
printf("Länge: %d\n", list_length(list));
list_free(list); /* leckt wegen unvollständigem TODO */
return 0;
}

View file

@ -0,0 +1,37 @@
# Demo-Protokoll: go-fibonacci
## Lauf 1
**Datum:**
**Befehl:**
```
/optimize "Ersetze die naive Rekursion durch Memoization.
fib(50) soll in unter 1ms abgeschlossen sein.
Bestehende Tests müssen weiterhin grün bleiben." \
--test-cmd "go test ./..." --interactive
```
**Startzeit:**
**Ende Loop (PASS):**
**Dauer Loop (min):**
**Runden:**
**Endergebnis Loop:** PASS / PASS WITH CONCERNS
**Befehl im --interactive-Checkpoint:**
```
/continue "Gib zusätzlich die Berechnungszeit in Mikrosekunden aus."
```
*(oder: `/continue` ohne Zusatzauftrag)*
**Startzeit /continue:**
**Ende /continue:**
**Befehl /shipit:**
```
/shipit
```
**Startzeit /shipit:**
**Endzeit /shipit:**
**Endergebnis /shipit:** SHIP / NO-SHIP
**Besonderheiten / Beobachtungen:**
---

View file

@ -0,0 +1,48 @@
# Go Fibonacci
Naive rekursive Fibonacci-Implementierung — korrekt, aber exponentiell langsam.
`fib(45)` dauert mehrere Sekunden; `fib(50)` läuft praktisch nicht durch.
## Aktueller Stand
```
main.go fib(n) — rekursiv, O(2^n)
main_test.go TestFib mit 5 Tabellen-Tests (alle grün)
```
## Demo 1: `/optimize --interactive`
```
/optimize "Ersetze die naive Rekursion durch Memoization.
fib(50) soll in unter 1ms abgeschlossen sein.
Bestehende Tests müssen weiterhin grün bleiben." \
--test-cmd "go test ./..." --interactive
```
Nach dem ersten PASS hält pi-coder im **interaktiven Checkpoint** an.
Hier kann ein Zusatzauftrag erteilt werden:
```
/continue "Gib zusätzlich die Berechnungszeit in Mikrosekunden aus."
```
Oder einfach bestätigen:
```
/continue
```
## Demo 2: Abschluss mit `/shipit`
```
/shipit
```
Der Judge prüft nochmals explizit auf Produktionsreife und gibt SHIP oder NO-SHIP zurück.
## Manueller Test
```bash
go test ./...
go run main.go
```

View file

@ -0,0 +1,3 @@
module fibonacci
go 1.21

View file

@ -0,0 +1,18 @@
package main
import "fmt"
// fib berechnet die n-te Fibonacci-Zahl rekursiv.
// Korrekt, aber für n > 40 sehr langsam (exponentiell).
func fib(n int) int {
if n <= 1 {
return n
}
return fib(n-1) + fib(n-2)
}
func main() {
for i := 0; i <= 10; i++ {
fmt.Printf("fib(%2d) = %d\n", i, fib(i))
}
}

View file

@ -0,0 +1,20 @@
package main
import "testing"
func TestFib(t *testing.T) {
cases := []struct {
n, want int
}{
{0, 0},
{1, 1},
{2, 1},
{5, 5},
{10, 55},
}
for _, c := range cases {
if got := fib(c.n); got != c.want {
t.Errorf("fib(%d) = %d, want %d", c.n, got, c.want)
}
}
}

View file

@ -0,0 +1,19 @@
# Demo-Protokoll: python-calculator
## Lauf 1
**Datum:**
**Befehl:**
```
/optimize "Ergänze multiply, divide (wirft ZeroDivisionError bei 0) und power.
Schreibe pytest-Tests für alle neuen Funktionen." \
--test-cmd "pytest test_calculator.py -v"
```
**Startzeit:**
**Endzeit:**
**Dauer (min):**
**Runden:**
**Endergebnis:** PASS / PASS WITH CONCERNS / SHIP / NO-SHIP
**Besonderheiten / Beobachtungen:**
---

View file

@ -0,0 +1,51 @@
# Python Calculator
Einfacher Taschenrechner mit `add()` und `subtract()`.
Multiply, divide, power und Fehlerbehandlung fehlen noch.
## Aktueller Stand
```
calculator.py add(), subtract()
test_calculator.py 5 pytest-Tests (alle grün)
```
## Demo: `/optimize` mit Test-Integration
```
/optimize "Ergänze multiply, divide (wirft ZeroDivisionError bei 0) und power.
Schreibe pytest-Tests für alle neuen Funktionen." \
--test-cmd "pytest test_calculator.py -v"
```
**Was pi-coder hier zeigt:**
- Coder implementiert, committet
- Extension führt `pytest` aus und übergibt das Ergebnis an den Judge
- Judge bewertet Korrektheit anhand der Testergebnisse
- Bei FAIL: Coder fixt, nächste Runde
## Voraussetzungen
```bash
pip install pytest
```
## Weitere Demo-Befehle nach dem `/optimize`-Lauf
```
/quick_check "Sind alle Randfälle (negative Zahlen, floats) korrekt behandelt?"
```
Schnelle Einzel-Beurteilung ohne neuen Fix-Loop.
```
/update_doku
```
Lässt den Coder Code-Kommentare ergänzen, README aktualisieren und eine
Bedienungsanleitung erzeugen.
## Manueller Test
```bash
pytest test_calculator.py -v
python calculator.py
```

View file

@ -0,0 +1,11 @@
def add(a, b):
return a + b
def subtract(a, b):
return a - b
if __name__ == "__main__":
print(add(3, 4)) # 7
print(subtract(10, 3)) # 7

View file

@ -0,0 +1,18 @@
import pytest
from calculator import add, subtract
def test_add_positive():
assert add(2, 3) == 5
def test_add_negative():
assert add(-1, 1) == 0
def test_add_zero():
assert add(0, 0) == 0
def test_subtract_basic():
assert subtract(5, 3) == 2
def test_subtract_negative_result():
assert subtract(3, 5) == -2

31
examples/restore-all.sh Executable file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Stellt den Ausgangszustand aller Examples wieder her.
# Löscht erzeugte Sub-Repos, restauriert Quelldateien aus dem Haupt-Repo
# und bereinigt Build-Artefakte.
set -euo pipefail
ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel)"
EXAMPLES="$ROOT/examples"
echo "Stelle Examples-Ausgangszustand wieder her..."
for dir in python-calculator rust-wordcount go-fibonacci c-linkedlist; do
path="$EXAMPLES/$dir"
if [ -d "$path/.git" ]; then
rm -rf "$path/.git"
echo " ✓ Sub-Repo entfernt: $dir"
fi
git -C "$ROOT" checkout -- "examples/$dir/"
echo " ✓ Dateien restauriert: $dir"
done
# Build-Artefakte bereinigen
rm -rf "$EXAMPLES/rust-wordcount/target"
find "$EXAMPLES" -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find "$EXAMPLES" -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
find "$EXAMPLES" -name "ll_demo" -delete 2>/dev/null || true
echo ""
echo "Fertig. Alle Examples sind im Ausgangszustand."
echo "git status zeigt examples/ als clean (wenn kein PROTOKOLL.md verändert wurde)."

View file

@ -0,0 +1,4 @@
[package]
name = "wordcount"
version = "0.1.0"
edition = "2021"

View file

@ -0,0 +1,29 @@
# Demo-Protokoll: rust-wordcount
## Lauf 1
**Datum:**
**Befehl:**
```
/optimize "Ergänze --lines (Zeilenzählung) und --chars (Zeichenzählung) als CLI-Flags.
Ohne Flag: Standardausgabe wie bisher (Wörter).
Schreibe Tests für alle drei Modi." \
--test-cmd "cargo test"
```
**Startzeit:**
**Endzeit:**
**Dauer /optimize (min):**
**Runden:**
**Endergebnis /optimize:** PASS / PASS WITH CONCERNS / SHIP / NO-SHIP
**Befehl /version:**
```
/version
```
**Startzeit /version:**
**Endzeit /version:**
**Gewählter Bump:** patch / minor / major
**Gesetzter Tag:**
**Besonderheiten / Beobachtungen:**
---

View file

@ -0,0 +1,50 @@
# Rust Word Counter
Liest stdin und gibt die Anzahl der Wörter aus.
Zeilen- und Zeichenzählung sowie CLI-Flags fehlen noch.
## Aktueller Stand
```
src/main.rs count_words() — nur Wortzählung, kein Argument-Parsing
Cargo.toml Version 0.1.0
```
## Demo 1: `/optimize` mit Cargo-Test-Integration
```
/optimize "Ergänze --lines (Zeilenzählung) und --chars (Zeichenzählung) als CLI-Flags.
Ohne Flag: Standardausgabe wie bisher (Wörter).
Schreibe Tests für alle drei Modi." \
--test-cmd "cargo test"
```
**Was pi-coder hier zeigt:**
- Rust-Toolchain wird automatisch erkannt
- `cargo test`-Output geht an den Judge
- Mehrere Compile-Test-Fix-Zyklen möglich
## Demo 2: `/version` nach dem Feature
**Voraussetzung:** Das Verzeichnis muss ein git-Repo mit mindestens einem Commit sein.
Falls noch kein Repo existiert, vorher einmalig:
```bash
git init && git add -A && git commit -m "feat: initial wordcount"
git tag v0.1.0
```
```
/version
```
Analysiert die Commits seit `v0.1.0`, erkennt `feat:`-Commits → schlägt `minor`-Bump vor
und setzt den Git-Tag `v0.2.0`.
## Manueller Test
```bash
cargo test
echo "Hallo Welt" | cargo run
echo -e "Zeile 1\nZeile 2" | cargo run -- --lines
```

View file

@ -0,0 +1,36 @@
use std::io::{self, Read};
fn count_words(text: &str) -> usize {
text.split_whitespace().count()
}
fn main() {
let mut input = String::new();
io::stdin().read_to_string(&mut input).unwrap();
println!("{} words", count_words(&input));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_input() {
assert_eq!(count_words(""), 0);
}
#[test]
fn test_single_word() {
assert_eq!(count_words("hallo"), 1);
}
#[test]
fn test_multiple_words() {
assert_eq!(count_words("eins zwei drei"), 3);
}
#[test]
fn test_extra_whitespace() {
assert_eq!(count_words(" a b "), 2);
}
}