feat: initial commit

This commit is contained in:
2025-07-05 00:07:04 +02:00
commit 2668db0f18
14 changed files with 3679 additions and 0 deletions

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

564
App.tsx Normal file
View File

@ -0,0 +1,564 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { Dialog } from "@headlessui/react";
import { Cog6ToothIcon } from "@heroicons/react/24/outline";
import { Exercise, CheckResult } from './types';
import { exercises as structExercises } from './data/exercises';
import { programmingExercises } from './data/programming_exercises';
import { GoogleGenAI, GenerateContentResponse } from "@google/genai";
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
import * as Diff from 'diff';
import Editor from 'react-simple-code-editor';
import Prism from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
import 'prismjs/components/prism-c';
// --- GEMINI API SETUP & HELPERS ---
// (ai instance will be created in App component)
const extractJson = (text: string): any => {
let jsonString = text.trim();
const fenceRegex = /^```(?:json)?\s*\n?(.*?)\n?\s*```$/s;
const match = jsonString.match(fenceRegex);
if (match && match[1]) {
jsonString = match[1].trim();
}
const lastBracket = jsonString.lastIndexOf('}');
if (lastBracket === -1) {
throw new Error("No closing brace '}' found in API response.");
}
let openBraceCount = 0;
let firstBracket = -1;
for (let i = lastBracket; i >= 0; i--) {
if (jsonString[i] === '}') {
openBraceCount++;
} else if (jsonString[i] === '{') {
openBraceCount--;
}
if (openBraceCount === 0) {
firstBracket = i;
break;
}
}
if (firstBracket === -1) {
throw new Error("Could not find matching opening brace '{' in API response.");
}
const finalJsonString = jsonString.substring(firstBracket, lastBracket + 1);
return JSON.parse(finalJsonString);
};
const evaluateWithGemini = async (
ai: GoogleGenAI,
userCode: string,
solutionCode: string,
prompt: string
): Promise<{ correct: boolean, explanation: string }> => {
const systemInstruction = `You are an expert C programming tutor. Your task is to evaluate a user's code against a model solution for a specific exercise.
The user's code might be functionally equivalent even if it looks different. Check for semantic correctness. For example, the order of members in a struct definition does not matter. Variable names can be different as long as they are used consistently and correctly. For programming tasks, focus on whether the user's code fulfills all requirements of the prompt.
The exercise part is: "${prompt}"
Analyze the user's C code and compare it to the provided solution.
Respond ONLY with a single JSON object in the format:
{
"correct": <boolean>,
"explanation": "<string>"
}
- "correct": true if the user's code is a valid and correct solution, false otherwise.
- "explanation": If correct, provide a brief confirmation. If incorrect, provide a clear, concise explanation of what is wrong. Use markdown for formatting, especially for code blocks. **Your entire explanation must be in German.**`;
const contents = `Here is the user's code:\n\`\`\`c\n${userCode}\n\`\`\`\n\nHere is the model solution for reference:\n\`\`\`c\n${solutionCode}\n\`\`\`\n\nPlease evaluate the user's code and provide your response in the specified JSON format.`;
const response: GenerateContentResponse = await ai.models.generateContent({
model: "gemini-2.5-flash",
contents: contents,
config: {
systemInstruction: systemInstruction,
responseMimeType: "application/json",
},
});
try {
const jsonResponse = extractJson(response.text);
if (typeof jsonResponse.correct !== 'boolean' || typeof jsonResponse.explanation !== 'string') {
throw new Error("Invalid JSON structure from API.");
}
return jsonResponse;
} catch (e) {
console.error("Failed to parse Gemini response:", e);
console.error("Raw response text:", response.text);
throw new Error(`Could not parse the evaluation response.`);
}
}
// --- UI ICONS ---
const CheckIcon: React.FC<{ className?: string }> = ({ className }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
</svg>
);
const CrossIcon: React.FC<{ className?: string }> = ({ className }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
);
const WarningIcon: React.FC<{ className?: string }> = ({ className }) => (
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
);
// --- SUB-COMPONENTS ---
const CodeEditor: React.FC<{
value: string;
onChange: (value: string) => void;
placeholder?: string;
}> = ({ value, onChange, placeholder }) => {
return (
<div className="bg-slate-900 rounded-lg border-2 border-slate-700 focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500 transition-all duration-200 overflow-hidden">
<Editor
value={value}
onValueChange={onChange}
highlight={code => Prism.highlight(code, Prism.languages.c, 'c')}
padding={12}
placeholder={placeholder}
textareaClassName="code-editor-textarea"
className="caret-white"
style={{
fontFamily: '"Roboto Mono", monospace',
fontSize: 14,
lineHeight: 1.5,
minHeight: '12rem',
color: '#f8f8f2',
}}
/>
</div>
);
};
interface CodeDiffViewerProps {
oldCode: string;
newCode: string;
}
const CodeDiffViewer: React.FC<CodeDiffViewerProps> = ({ oldCode, newCode }) => {
const differences = Diff.diffLines(oldCode.trim(), newCode.trim());
return (
<pre className="bg-slate-800 text-slate-200 p-3 rounded-md font-mono text-xs max-h-64 overflow-auto">
<code>
{differences.map((part, index) => {
const className = part.added
? 'bg-green-500/20'
: part.removed
? 'bg-red-500/20'
: '';
const prefix = part.added ? '+' : part.removed ? '-' : ' ';
const lines = part.value.replace(/\n$/, '').split('\n');
return (
<span key={index} className={`block ${className}`}>
{lines.map((line, i) => (
<div key={i} className="flex">
<span className={`w-5 flex-shrink-0 text-left pl-1 ${part.added ? 'text-green-400' : part.removed ? 'text-red-400' : 'text-slate-500'}`}>{prefix}</span>
<span className="flex-grow whitespace-pre-wrap">{line}</span>
</div>
))}
</span>
);
})}
</code>
</pre>
);
};
interface ExerciseSheetProps {
exercise: Exercise;
onNext: () => void;
ai: GoogleGenAI | null;
}
const ExerciseSheet: React.FC<ExerciseSheetProps> = ({ exercise, onNext, ai }) => {
const [inputs, setInputs] = useState<Record<string, string>>({});
const [results, setResults] = useState<CheckResult[] | null>(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setInputs({});
setResults(null);
setIsLoading(false);
}, [exercise]);
const handleInputChange = (partId: string, value: string) => {
setInputs(prev => ({ ...prev, [partId]: value }));
};
const handleSubmit = async () => {
setIsLoading(true);
setResults(null);
const partsToEvaluate = exercise.parts.map(part => ({
...part,
userInput: inputs[part.id] || '',
}));
const evaluationPromises = partsToEvaluate.map(part => {
if (!part.userInput.trim()) {
return Promise.resolve({
partId: part.id,
isCorrect: false,
explanation: "Keine Eingabe gemacht."
});
}
if (!ai || typeof ai.models !== "object" || !ai.models.generateContent) {
return Promise.resolve({
partId: part.id,
isCorrect: false,
explanation: "API-Key fehlt oder ungültig, oder Gemini SDK nicht korrekt geladen.",
error: "Gemini API nicht verfügbar"
});
}
return evaluateWithGemini(ai, part.userInput, part.solution, part.prompt)
.then(evalResult => ({
partId: part.id,
isCorrect: evalResult.correct,
explanation: evalResult.explanation,
}))
.catch(e => ({
partId: part.id,
isCorrect: false,
explanation: '',
error: e instanceof Error ? e.message : 'An unknown error occurred.'
}));
});
const finalResults = await Promise.all(evaluationPromises);
setResults(finalResults);
setIsLoading(false);
};
const handleReset = () => {
setInputs({});
setResults(null);
setIsLoading(false);
}
const handleNextExercise = () => {
onNext();
}
const hasEvaluationErrors = results?.some(r => r.error);
return (
<div className="bg-white rounded-lg shadow-xl p-8 md:p-12 font-sans w-full relative">
{isLoading && (
<div className="absolute inset-0 bg-white/80 backdrop-blur-sm flex flex-col items-center justify-center z-20 rounded-lg">
<svg className="animate-spin h-12 w-12 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<p className="mt-4 font-serif text-lg text-slate-700">Evaluating your code with Gemini...</p>
</div>
)}
<header className="border-b pb-4 mb-6">
<h1 className="font-serif text-2xl md:text-3xl font-bold text-slate-800">{exercise.title}</h1>
<p className="text-slate-500">Prüfung Programmieren - C</p>
</header>
{!results ? (
// --- EXERCISE VIEW ---
<div className="space-y-8">
{exercise.parts.map(part => (
<div key={part.id} className="flex flex-col md:flex-row gap-4">
<div className="w-full md:w-2/5">
<h2 className="font-serif font-bold text-lg text-slate-700">Teil {part.id}) ({part.points}P)</h2>
<div className="mt-2 prose prose-slate prose-sm max-w-none prose-th:text-slate-900 prose-td:text-slate-800">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{part.prompt}</ReactMarkdown>
</div>
</div>
<div className="w-full md:w-3/5">
<CodeEditor
value={inputs[part.id] || ''}
onChange={(value) => handleInputChange(part.id, value)}
placeholder={`// Code für Teil ${part.id}) hier eingeben...`}
/>
</div>
</div>
))}
<div className="flex justify-end pt-6">
<button onClick={handleSubmit} disabled={isLoading} className="px-8 py-3 bg-blue-600 text-white font-bold rounded-lg hover:bg-blue-700 transition-transform transform hover:scale-105 shadow-lg disabled:bg-slate-400 disabled:scale-100 disabled:cursor-not-allowed">
Abgeben & Prüfen
</button>
</div>
</div>
) : (
// --- RESULT VIEW ---
<div className="space-y-6">
<h2 className="font-serif text-2xl font-bold text-slate-800 border-b pb-2">Auswertung</h2>
{results.map(result => {
const part = exercise.parts.find(p => p.id === result.partId)!;
const userInput = inputs[part.id] || '';
if (result.error) {
return (
<div key={result.partId} className="p-4 rounded-lg border border-amber-500 bg-amber-50">
<div className="flex items-center gap-3 mb-2">
<WarningIcon className="w-8 h-8 text-amber-600 bg-amber-100 rounded-full p-1" />
<h3 className="font-serif font-bold text-xl text-amber-800">Teil {part.id}) - Evaluation Failed</h3>
</div>
<div className="pl-11">
<p className="text-sm text-amber-700 mb-4">Error: {result.error}</p>
<h4 className="font-bold text-sm mb-1 text-slate-600">Ihre Eingabe:</h4>
<pre className="bg-slate-100 text-slate-800 p-3 rounded-md font-mono text-xs max-h-48 overflow-auto">{userInput || '(Keine Eingabe)'}</pre>
</div>
</div>
);
}
return (
<div key={result.partId} className={`p-4 rounded-lg border ${result.isCorrect ? 'border-green-500 bg-green-50' : 'border-red-500 bg-red-50'}`}>
<div className="flex items-center gap-3 mb-2 flex-wrap">
{result.isCorrect ? <CheckIcon className="w-6 h-6 text-green-600" /> : <CrossIcon className="w-6 h-6 text-red-600" />}
<h3 className="font-serif font-bold text-xl text-slate-700">Teil {part.id})</h3>
<span className={`px-3 py-1 text-xs font-bold rounded-full ${result.isCorrect ? 'bg-green-200 text-green-800' : 'bg-red-200 text-red-800'}`}>
{result.isCorrect ? "Korrekt" : "Fehlerhaft"}
</span>
</div>
<div className="prose prose-slate prose-sm max-w-none pl-9 prose-th:text-slate-900 prose-td:text-slate-800">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{result.explanation}</ReactMarkdown>
</div>
{!result.isCorrect && userInput.trim() && (
<div className="mt-4 pl-9">
<h4 className="font-bold text-sm mb-2 text-slate-600">Vergleich Ihrer Eingabe mit der Musterlösung:</h4>
<div className="flex gap-2 text-xs mb-2 items-center">
<span className="flex items-center gap-1.5 px-2 py-0.5 bg-red-500/20 text-red-300 rounded"><span className="font-mono bg-red-900/50 text-red-200 rounded-full w-4 h-4 text-center leading-4">-</span> Ihre Eingabe</span>
<span className="flex items-center gap-1.5 px-2 py-0.5 bg-green-500/20 text-green-300 rounded"><span className="font-mono bg-green-900/50 text-green-200 rounded-full w-4 h-4 text-center leading-4">+</span> Korrekte Lösung</span>
</div>
<CodeDiffViewer oldCode={userInput} newCode={part.solution} />
</div>
)}
</div>
);
})}
{hasEvaluationErrors && (
<div className="mt-6 p-4 rounded-md bg-yellow-100 border border-yellow-300 text-center">
<p className="text-yellow-800 font-medium">Bei einigen Teilen ist ein Fehler bei der Auswertung aufgetreten. Sie können es erneut versuchen.</p>
</div>
)}
<div className="mt-8 pt-6 border-t">
<div className="prose prose-slate prose-sm max-w-none prose-th:text-slate-900 prose-td:text-slate-800">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{exercise.explanation}</ReactMarkdown>
</div>
</div>
<div className="flex justify-between items-center pt-6 flex-wrap gap-4">
<button onClick={handleReset} className="px-6 py-2 bg-slate-200 text-slate-800 font-bold rounded-lg hover:bg-slate-300 transition">
Nochmal versuchen
</button>
{hasEvaluationErrors && (
<button onClick={handleSubmit} disabled={isLoading} className="px-8 py-3 bg-amber-500 text-white font-bold rounded-lg hover:bg-amber-600 transition-transform transform hover:scale-105 shadow-lg disabled:bg-slate-400 disabled:scale-100">
Erneut auswerten
</button>
)}
<button onClick={handleNextExercise} className="px-8 py-3 bg-green-600 text-white font-bold rounded-lg hover:bg-green-700 transition-transform transform hover:scale-105 shadow-lg">
Nächste Aufgabe
</button>
</div>
</div>
)}
</div>
);
};
// --- MAIN APP COMPONENT ---
type Category = 'structs' | 'programming';
const exerciseData: Record<Category, Exercise[]> = {
structs: structExercises,
programming: programmingExercises
};
export default function App() {
const [exerciseSets, setExerciseSets] = useState<Record<Category, Exercise[]>>({structs: [], programming: []});
const [category, setCategory] = useState<Category>('structs');
const [currentIndex, setCurrentIndex] = useState(0);
// --- API KEY STATE ---
const [apiKey, setApiKey] = useState<string>('');
const [showSettings, setShowSettings] = useState(false);
const [inputKey, setInputKey] = useState('');
// On mount, load API key from localStorage or env
useEffect(() => {
const stored = localStorage.getItem('gemini_api_key');
if (stored) {
setApiKey(stored);
} else if (process.env.API_KEY) {
setApiKey(process.env.API_KEY);
}
}, []);
// Gemini API instance (re-created if key changes)
const ai = useMemo(() => apiKey ? new GoogleGenAI({ apiKey }) : null, [apiKey]);
// Save API key to localStorage and state
const handleSaveKey = () => {
localStorage.setItem('gemini_api_key', inputKey);
setApiKey(inputKey);
setShowSettings(false);
};
// UI logic
useEffect(() => {
if (showSettings) setInputKey(apiKey || '');
}, [showSettings, apiKey]);
useEffect(() => {
const shuffledStructs = [...exerciseData.structs].sort(() => Math.random() - 0.5);
const shuffledProgramming = [...exerciseData.programming].sort(() => Math.random() - 0.5);
setExerciseSets({
structs: shuffledStructs,
programming: shuffledProgramming
});
}, []);
const handleCategoryChange = (newCategory: Category) => {
if (newCategory !== category) {
setCategory(newCategory);
setCurrentIndex(0);
}
}
const exercisesForCategory = useMemo(() => exerciseSets[category], [exerciseSets, category]);
const handleNext = useCallback(() => {
setCurrentIndex(prev => (prev + 1) % (exercisesForCategory.length || 1));
}, [exercisesForCategory.length]);
const currentExercise = useMemo(() => exercisesForCategory[currentIndex], [exercisesForCategory, currentIndex]);
if (exercisesForCategory.length === 0 || !currentExercise) {
return <div className="flex items-center justify-center h-screen text-xl font-serif">Lade Übungen...</div>;
}
const getTabClass = (tabCategory: Category) => {
const isActive = category === tabCategory;
return `px-4 py-3 text-sm md:text-base font-medium transition-all duration-200 border-b-2 focus:outline-none ${
isActive
? 'border-blue-600 text-blue-600'
: 'border-transparent text-slate-500 hover:text-slate-800 hover:border-slate-300'
}`;
}
const getExerciseButtonTitle = (ex: Exercise) => {
const parts = ex.title.split(/ - |: /);
return (parts.pop() || ex.title).trim();
}
return (
<div className="min-h-screen flex flex-col p-4 md:p-8">
{/* Settings Button */}
<button
className="fixed top-4 right-4 z-50 bg-white rounded-full p-2 shadow hover:bg-slate-100 transition"
aria-label="API Key Settings"
onClick={() => setShowSettings(true)}
>
<Cog6ToothIcon className="w-7 h-7 text-slate-600" />
</button>
{/* Settings Modal */}
<Dialog open={showSettings} onClose={() => setShowSettings(false)} className="relative z-50">
<div className="fixed inset-0 bg-black/30" aria-hidden="true" />
<div className="fixed inset-0 flex items-center justify-center p-4">
<Dialog.Panel className="bg-white rounded-lg shadow-xl p-8 max-w-md w-full">
<Dialog.Title className="font-bold text-lg mb-4 flex items-center gap-2">
<Cog6ToothIcon className="w-6 h-6 text-blue-600" />
Gemini API Key
</Dialog.Title>
<div className="mb-4">
<input
type="text"
className="w-full border rounded px-3 py-2 text-slate-800"
value={inputKey}
onChange={e => setInputKey(e.target.value)}
placeholder="Enter Gemini API Key"
/>
</div>
<div className="flex justify-end gap-2">
<button
className="px-4 py-2 rounded bg-slate-200 text-slate-700 hover:bg-slate-300"
onClick={() => setShowSettings(false)}
>
Cancel
</button>
<button
className="px-4 py-2 rounded bg-blue-600 text-white font-bold hover:bg-blue-700"
onClick={handleSaveKey}
disabled={!inputKey.trim()}
>
Save
</button>
</div>
<div className="mt-4 text-xs text-slate-500">
Your API key is stored in your browser only.
</div>
</Dialog.Panel>
</div>
</Dialog>
<header className="mb-6 flex-shrink-0">
<nav className="flex space-x-1 border-b border-slate-200">
<button onClick={() => handleCategoryChange('structs')} className={getTabClass('structs')}>
Structs
</button>
<button onClick={() => handleCategoryChange('programming')} className={getTabClass('programming')}>
Programmieren
</button>
</nav>
</header>
<div className="flex flex-col md:flex-row gap-8 flex-grow min-h-0">
<aside className="w-full md:w-64 flex-shrink-0">
<h2 className="font-serif text-xl font-bold text-slate-700 mb-4 capitalize">{category} Übungen</h2>
<nav className="space-y-2">
{exercisesForCategory.map((ex, index) => (
<button
key={ex.id}
onClick={() => setCurrentIndex(index)}
className={`w-full text-left px-4 py-2 rounded-md transition duration-150 text-sm ${
ex.id === currentExercise.id
? 'bg-blue-600 text-white font-bold shadow'
: 'bg-white text-slate-700 hover:bg-slate-200'
}`}
>
{getExerciseButtonTitle(ex)}
</button>
))}
</nav>
</aside>
<main className="flex-grow flex items-start justify-center min-w-0">
<div className="w-full max-w-4xl">
<ExerciseSheet exercise={currentExercise} onNext={handleNext} ai={ai} />
</div>
</main>
</div>
</div>
);
}

14
README.md Normal file
View File

@ -0,0 +1,14 @@
# Run and deploy your AI Studio app
This contains everything you need to run your app locally.
## Run Locally
**Prerequisites:** Node.js
1. Install dependencies:
`npm install`
2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
3. Run the app:
`npm run dev`

565
data/exercises.ts Normal file
View File

@ -0,0 +1,565 @@
import { Exercise } from '../types';
export const exercises: Exercise[] = [
{
id: 'ex1',
title: 'Aufgabe 7: Datenstruktur - Dictionary',
parts: [
{
id: 'a',
prompt: 'Definieren Sie in der Programmiersprache C eine Struktur `dictionary` mit drei Komponenten:\n- `key` für ein Wort mit bis zu 40 Zeichen\n- `answer` für einen Satz mit bis zu 100 Zeichen\n- `random` für ganzzahlige Zufallswerte\n\nDefinieren Sie dann eine neuen Datentyp `DICT` für diese Struktur.',
solution: 'typedef struct dictionary {\n char key[40];\n char answer[100];\n int random;\n} DICT;',
points: 5,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Initialisieren Sie ein Array `chatGPT` vom Datentyp `DICT` mit 3 Einträgen mit den Werten der nachfolgenden Tabelle:\n| key | answer | random |\n|---|---|---|\n| hello | hi | 1 |\n| you | me? | 2 |\n| I | you? | 3 |',
solution: 'DICT chatGPT[3] = {\n {"hello", "hi", 1},\n {"you", "me?", 2},\n {"I", "you?", 3}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Setzen Sie einen Strukturzeiger `p_chat` auf den dritten Struktureintrag aus Teil b) und setzen Sie die Komponente `random` auf einen zufälligen Wert 0 bis 3. (`rand() % 4` liefert Zufallswerte)',
solution: 'DICT *p_chat = &chatGPT[2];\np_chat->random = rand() % 4;',
points: 1,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct dictionary {\n char key[40];\n char answer[100];\n int random;\n} DICT;\n```\n- `typedef struct ... NAME;` wird verwendet, um einen neuen Datentyp zu definieren. Das ist kürzer als jedes Mal `struct dictionary` zu schreiben.\n- `char key[40];` deklariert ein Zeichen-Array (String) für das `key`-Feld.\n- `int random;` deklariert ein einfaches Integer für den `random`-Wert.\n- Die Reihenfolge der Elemente in der Struktur ist für die Funktionalität nicht relevant, aber es ist guter Stil, sie logisch zu gruppieren.\n\n**Teil b)**\n```c\nDICT chatGPT[3] = {\n {"hello", "hi", 1},\n {"you", "me?", 2},\n {"I", "you?", 3}\n};\n```\n- `DICT chatGPT[3]` deklariert ein Array mit 3 Elementen vom Typ `DICT`.\n- Die Initialisierung `{ ... }` verwendet geschweifte Klammern für jedes Strukturelement im Array, wobei die Werte in der Reihenfolge der Strukturdefinition angegeben werden.\n\n**Teil c)**\n```c\nDICT *p_chat = &chatGPT[2];\np_chat->random = rand() % 4;\n```\n- `DICT *p_chat` deklariert einen Zeiger auf eine `DICT`-Struktur.\n- `&chatGPT[2]` holt die Speicheradresse des dritten Elements im Array (Indizes sind 0-basiert).\n- Der `->` Operator wird verwendet, um auf Strukturelemente über einen Zeiger zuzugreifen. `p_chat->random` ist äquivalent zu `(*p_chat).random`.'
},
{
id: 'ex2',
title: 'Aufgabe 7: Datenstruktur - Bundesliga',
parts: [
{
id: 'a',
prompt: 'Definieren Sie in C einen eigenen Datentyp `BUNDESLIGA` und gleichzeitig einen Strukturzeigertyp `PBUNDESLIGA` zu einer unbenannten Struktur mit:\n- `verein`: Zeichenkette mit 20 Buchstaben\n- `platz`: ganze, vorzeichenlose Zahl\n- `torv`: Datentyp `struct torverhaeltnis` (vorgegeben: `struct torverhaeltnis{ int tore; int gegentore;};`)',
solution: 'struct torverhaeltnis{ int tore; int gegentore;};\ntypedef struct {\n char verein[20];\n unsigned int platz;\n struct torverhaeltnis torv;\n} BUNDESLIGA, *PBUNDESLIGA;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `podest` mit 3 Einträgen und initialisieren Sie es. Deklarieren Sie einen Zeiger `ppodest` und lassen Sie ihn auf das Array zeigen.\n| Vereinsname | Platz | Torverhältnis |\n|---|---|---|\n| Bayern Muenchen | 1 | 92 zu 68 |\n| Borussia Dortmund | 2 | 83 zu 44 |\n| RB Leipzig | 3 | 64 zu 41 |',
solution: 'BUNDESLIGA podest[3] = {\n {"Bayern Muenchen", 1, {92, 68}},\n {"Borussia Dortmund", 2, {83, 44}},\n {"RB Leipzig", 3, {64, 41}}\n};\nPBUNDESLIGA ppodest = podest;',
points: 5,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Geben Sie mithilfe der `printf()`-Funktion den Eintrag `torv` von Borussia Dortmund in der Formatierung `83:44` aus. Verwenden Sie hierzu den Zeiger `ppodest`.',
solution: 'printf("%d:%d", (ppodest + 1)->torv.tore, (ppodest + 1)->torv.gegentore);',
points: 2,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\nstruct torverhaeltnis{ int tore; int gegentore;};\ntypedef struct {\n char verein[20];\n unsigned int platz;\n struct torverhaeltnis torv;\n} BUNDESLIGA, *PBUNDESLIGA;\n```\n- Eine anonyme Struktur wird verwendet (`struct { ... }`), da der Name der Struktur selbst nicht benötigt wird, nur der `typedef`.\n- `typedef struct { ... } BUNDESLIGA, *PBUNDESLIGA;` ist eine Abkürzung, um sowohl den Datentyp als auch einen Zeigertyp zu definieren.\n- `unsigned int` wird für `platz` verwendet, da Tabellenplätze nicht negativ sind.\n- Die `struct torverhaeltnis` ist verschachtelt.\n\n**Teil b)**\n```c\nBUNDESLIGA podest[3] = {\n {"Bayern Muenchen", 1, {92, 68}},\n {"Borussia Dortmund", 2, {83, 44}},\n {"RB Leipzig", 3, {64, 41}}\n};\nPBUNDESLIGA ppodest = podest;\n```\n- Bei der Initialisierung einer verschachtelten Struktur werden innere geschweifte Klammern `{}` verwendet: `{92, 68}` für `torv`.\n- Ein Zeiger auf ein Array zu setzen (`PBUNDESLIGA ppodest = podest;`) lässt den Zeiger auf das erste Element des Arrays (`&podest[0]`) zeigen.\n\n**Teil c)**\n```c\nprintf("%d:%d", (ppodest + 1)->torv.tore, (ppodest + 1)->torv.gegentore);\n```\n- Zeigerarithmetik wird verwendet, um auf das zweite Element zuzugreifen: `ppodest + 1`.\n- `(ppodest + 1)->torv` greift auf die verschachtelte Struktur zu.\n- `.tore` und `.gegentore` werden dann von dieser inneren Struktur aus mit dem Punkt-Operator aufgerufen.'
},
{
id: 'ex3',
title: 'Aufgabe: Studentendatenbank',
parts: [
{
id: 'a',
prompt: 'Definieren Sie eine Struktur `student` mit den Elementen `name` (Zeichenkette, max. 50), `student_id` (Integer) und `grades` (Array von 5 Integern). Erstellen Sie einen neuen Datentyp `STUDENT` und einen Zeigertyp `PSTUDENT` dafür.',
solution: 'typedef struct student {\n char name[50];\n int student_id;\n int grades[5];\n} STUDENT, *PSTUDENT;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren und initialisieren Sie ein Array `klasse` vom Typ `STUDENT` mit zwei Studenten:\n1. Max Mustermann, ID 12345, Noten: 1, 2, 1, 3, 2\n2. Erika Mustermann, ID 54321, Noten: 2, 2, 3, 1, 1',
solution: 'STUDENT klasse[2] = {\n {"Max Mustermann", 12345, {1, 2, 1, 3, 2}},\n {"Erika Mustermann", 54321, {2, 2, 3, 1, 1}}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_student` vom Typ `PSTUDENT`, der auf den zweiten Studenten im `klasse`-Array zeigt. Ändern Sie die dritte Note dieses Studenten auf `1`.',
solution: 'PSTUDENT p_student = &klasse[1];\np_student->grades[2] = 1;',
points: 2,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct student {\n char name[50];\n int student_id;\n int grades[5];\n} STUDENT, *PSTUDENT;\n```\n- Die Struktur `student` kombiniert verschiedene Datentypen.\n- `grades[5]` ist ein Array als Strukturelement.\n- `*PSTUDENT` definiert `PSTUDENT` als Zeigertyp auf die `STUDENT`-Struktur.\n\n**Teil b)**\n```c\nSTUDENT klasse[2] = {\n {"Max Mustermann", 12345, {1, 2, 1, 3, 2}},\n {"Erika Mustermann", 54321, {2, 2, 3, 1, 1}}\n};\n```\n- Das Array von Strukturen wird initialisiert.\n- `{1, 2, 1, 3, 2}` ist die Initialisierungsliste für das `grades`-Array innerhalb der Struktur.\n\n**Teil c)**\n```c\nPSTUDENT p_student = &klasse[1];\np_student->grades[2] = 1;\n```\n- Der Zeiger `p_student` wird mit dem Typ `PSTUDENT` deklariert.\n- `&klasse[1]` holt die Adresse des zweiten Elements.\n- `p_student->grades` greift auf das Noten-Array zu. `[2]` wählt das dritte Element aus, das dann überschrieben wird.'
},
{
id: 'ex4',
title: 'Aufgabe: Punkt im 3D-Raum',
parts: [
{
id: 'a',
prompt: 'Definieren Sie mittels `typedef` einen Datentyp `POINT3D` und einen Zeigertyp `PPOINT3D` für eine anonyme Struktur, die drei `double`-Werte `x`, `y` und `z` enthält.',
solution: 'typedef struct {\n double x;\n double y;\n double z;\n} POINT3D, *PPOINT3D;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren und initialisieren Sie zwei Variablen `p1` und `p2` vom Typ `POINT3D` mit beliebigen Werten.',
solution: 'POINT3D p1 = {10.5, -5.0, 3.2};\nPOINT3D p2 = {-2.0, 4.1, 7.8};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_p2` vom Typ `PPOINT3D`, der auf die Variable `p2` zeigt. Geben Sie die z-Koordinate von `p2` mittels des Zeigers und `printf` aus.',
solution: 'PPOINT3D p_p2 = &p2;\nprintf("Z-Koordinate: %f", p_p2->z);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n double x;\n double y;\n double z;\n} POINT3D, *PPOINT3D;\n```\n- Eine anonyme Struktur wird direkt mit `typedef` verwendet, um den Typ `POINT3D` und den Zeigertyp `PPOINT3D` zu erstellen.\n\n**Teil b)**\n```c\nPOINT3D p1 = {10.5, -5.0, 3.2};\nPOINT3D p2 = {-2.0, 4.1, 7.8};\n```\n- Variablen vom Struktur-Typ werden mit einer Initialisierungsliste `{}` deklariert.\n\n**Teil c)**\n```c\nPPOINT3D p_p2 = &p2;\nprintf("Z-Koordinate: %f", p_p2->z);\n```\n- Der Zeiger `p_p2` wird mit dem Typ `PPOINT3D` und der Adresse von `p2` initialisiert.\n- `p_p2->z` ist die korrekte Schreibweise, um über einen Zeiger auf ein Strukturelement zuzugreifen. Der Format-Specifier für `double` ist `%f`.'
},
{
id: 'ex5',
title: 'Aufgabe: Buchinventar',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `BOOK` und einen Zeigertyp `PBOOK` für eine Struktur mit:\n- `title` (max. 100 Zeichen)\n- `author` (max. 50 Zeichen)\n- `isbn` (long integer)\n- `stock` (vorzeichenlose Ganzzahl)',
solution: 'typedef struct {\n char title[100];\n char author[50];\n long isbn;\n unsigned int stock;\n} BOOK, *PBOOK;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `inventory` mit 2 Büchern und initialisieren Sie es mit fiktiven Daten.',
solution: 'BOOK inventory[2] = {\n {"The C Programming Language", "Kernighan & Ritchie", 9780131103627, 10},\n {"Clean Code", "Robert C. Martin", 9780132350884, 5}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Erstellen Sie einen Zeiger `p_book` vom Typ `PBOOK`, der auf das zweite Buch im `inventory` zeigt. Reduzieren Sie dessen `stock` um 1.',
solution: 'PBOOK p_book = &inventory[1];\np_book->stock--;',
points: 2,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n char title[100];\n char author[50];\n long isbn;\n unsigned int stock;\n} BOOK, *PBOOK;\n```\n- Die Struktur fasst relevante Informationen zu einem Buch zusammen. `long` wird für die ISBN verwendet, um große Zahlen zu speichern.\n- `*PBOOK` definiert den Zeigertyp `PBOOK`.\n\n**Teil b)**\n```c\nBOOK inventory[2] = {\n {"The C Programming Language", "Kernighan & Ritchie", 9780131103627, 10},\n {"Clean Code", "Robert C. Martin", 9780132350884, 5}\n};\n```\n- Ein Array von Strukturen wird deklariert und mit den Daten für zwei Bücher initialisiert.\n\n**Teil c)**\n```c\nPBOOK p_book = &inventory[1];\np_book->stock--;\n```\n- Der Zeiger `p_book` vom Typ `PBOOK` zeigt auf das Element mit Index 1 (das zweite Buch).\n- Der Dekrement-Operator `--` kann direkt auf das Strukturelement `stock` über den Zeiger angewendet werden.'
},
{
id: 'ex6',
title: 'Aufgabe: Computerspezifikationen',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `PC` und einen Zeigertyp `PPC` für eine Struktur `computer` mit:\n- `cpu_speed_ghz` (Fließkommazahl)\n- `ram_gb` (Ganzzahl)\n- `storage_gb` (Ganzzahl)',
solution: 'typedef struct computer {\n float cpu_speed_ghz;\n int ram_gb;\n int storage_gb;\n} PC, *PPC;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `office_pcs` mit 2 Einträgen vom Typ `PC` und initialisieren Sie es.',
solution: 'PC office_pcs[2] = {\n {3.4, 16, 512},\n {2.9, 8, 256}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_pc` vom Typ `PPC`, der auf den ersten PC im Array zeigt. Geben Sie dessen `ram_gb` mit `printf` aus.',
solution: 'PPC p_pc = office_pcs;\nprintf("RAM: %d GB", p_pc->ram_gb);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct computer {\n float cpu_speed_ghz;\n int ram_gb;\n int storage_gb;\n} PC, *PPC;\n```\n- `float` ist für die CPU-Geschwindigkeit geeignet.\n- Gleichzeitig wird der Zeigertyp `PPC` (äquivalent zu `PC*`) definiert.\n\n**Teil b)**\n```c\nPC office_pcs[2] = {\n {3.4, 16, 512},\n {2.9, 8, 256}\n};\n```\n- Initialisierung eines Arrays von Strukturen.\n\n**Teil c)**\n```c\nPPC p_pc = office_pcs;\nprintf("RAM: %d GB", p_pc->ram_gb);\n```\n- Der Zeiger `p_pc` wird mit dem Typ `PPC` deklariert. Eine Zuweisung auf den Array-Namen (`office_pcs`) lässt ihn auf das erste Element zeigen.\n- `p_pc->ram_gb` greift auf den Arbeitsspeicher des ersten PCs zu.'
},
{
id: 'ex7',
title: 'Aufgabe: Mitarbeiterakte (verschachtelt)',
parts: [
{
id: 'a',
prompt: 'Definieren Sie eine Struktur `personal_info` mit `name` (max. 50 Zeichen) und `address` (max. 100 Zeichen). Definieren Sie dann eine Struktur `employee`, die eine `employee_id` (long), ein `salary` (double) und eine verschachtelte Struktur vom Typ `personal_info` mit dem Namen `contact` enthält. Erstellen Sie den Datentyp `EMPLOYEE` und den Zeigertyp `PEMPLOYEE`.',
solution: 'struct personal_info {\n char name[50];\n char address[100];\n};\ntypedef struct employee {\n long employee_id;\n double salary;\n struct personal_info contact;\n} EMPLOYEE, *PEMPLOYEE;',
points: 4,
type: 'struct-definition',
},
{
id: 'b',
prompt: 'Deklarieren und initialisieren Sie eine Variable `new_hire` vom Typ `EMPLOYEE` mit fiktiven Daten.',
solution: 'EMPLOYEE new_hire = {\n 1001,\n 65000.50,\n {"John Doe", "123 Main St, Anytown"}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_emp` vom Typ `PEMPLOYEE` auf `new_hire`. Geben Sie die Adresse des Mitarbeiters über den Zeiger aus.',
solution: 'PEMPLOYEE p_emp = &new_hire;\nprintf("Address: %s", p_emp->contact.address);',
points: 2,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\nstruct personal_info {\n char name[50];\n char address[100];\n};\ntypedef struct employee {\n long employee_id;\n double salary;\n struct personal_info contact;\n} EMPLOYEE, *PEMPLOYEE;\n```\n- Dies zeigt die Definition einer verschachtelten Struktur. `employee` enthält eine Variable vom Typ `struct personal_info`.\n- Der `typedef` erstellt `EMPLOYEE` für die Struktur und `PEMPLOYEE` für einen Zeiger darauf.\n\n**Teil b)**\n```c\nEMPLOYEE new_hire = {\n 1001,\n 65000.50,\n {"John Doe", "123 Main St, Anytown"}\n};\n```\n- Bei der Initialisierung einer Struktur, die eine andere Struktur enthält, werden verschachtelte `{}` für die innere Struktur verwendet.\n\n**Teil c)**\n```c\nPEMPLOYEE p_emp = &new_hire;\nprintf("Address: %s", p_emp->contact.address);\n```\n- Der Zeiger `p_emp` wird mit dem Typ `PEMPLOYEE` deklariert.\n- `p_emp->contact` greift auf die innere `personal_info`-Struktur zu. Von dort aus wird mit `.address` auf das `address`-Feld zugegriffen.'
},
{
id: 'ex8',
title: 'Aufgabe: Musik-Track',
parts: [
{
id: 'a',
prompt: 'Definieren Sie via `typedef` einen Datentyp `TRACK` und einen Zeigertyp `PTRACK` für eine anonyme Struktur mit `title` (max. 60), `artist` (max. 40) und `duration_seconds` (int).',
solution: 'typedef struct {\n char title[60];\n char artist[40];\n int duration_seconds;\n} TRACK, *PTRACK;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `playlist` vom Typ `TRACK` mit 3 Einträgen und initialisieren Sie es.',
solution: 'TRACK playlist[3] = {\n {"Bohemian Rhapsody", "Queen", 355},\n {"Stairway to Heaven", "Led Zeppelin", 482},\n {"Hotel California", "Eagles", 391}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Lassen Sie einen Zeiger `p_track` vom Typ `PTRACK` auf den letzten Track der Playlist zeigen. Geben Sie Titel und Dauer dieses Tracks mit `printf` aus.',
solution: 'PTRACK p_track = &playlist[2];\nprintf("Now playing: %s (%ds)", p_track->title, p_track->duration_seconds);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n char title[60];\n char artist[40];\n int duration_seconds;\n} TRACK, *PTRACK;\n```\n- Eine typische Struktur zur Speicherung von Daten, ergänzt um den Zeiger-`typedef` `PTRACK`.\n\n**Teil b)**\n```c\nTRACK playlist[3] = {\n {"Bohemian Rhapsody", "Queen", 355},\n {"Stairway to Heaven", "Led Zeppelin", 482},\n {"Hotel California", "Eagles", 391}\n};\n```\n- Das Array wird mit den Daten der drei Songs initialisiert.\n\n**Teil c)**\n```c\nPTRACK p_track = &playlist[2];\nprintf("Now playing: %s (%ds)", p_track->title, p_track->duration_seconds);\n```\n- Der Zeiger `p_track` wird mit dem Typ `PTRACK` deklariert.\n- `&playlist[2]` gibt die Adresse des dritten (letzten) Elements an, da die Indizierung bei 0 beginnt.\n- `printf` kann mehrere Elemente einer Struktur ausgeben, die über einen Zeiger abgerufen werden.'
},
{
id: 'ex9',
title: 'Aufgabe: Rezept-Zutaten',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `INGREDIENT` und Zeigertyp `PINGREDIENT` für eine Struktur `ingredient` mit `name` (max. 30), `quantity` (float) und `unit` (max. 10).',
solution: 'typedef struct ingredient {\n char name[30];\n float quantity;\n char unit[10];\n} INGREDIENT, *PINGREDIENT;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `recipe_steps` mit 3 Zutaten und initialisieren Sie es (z.B. für Pfannkuchen: Mehl, Eier, Milch).',
solution: 'INGREDIENT recipe_steps[3] = {\n {"Mehl", 250.0, "g"},\n {"Eier", 2.0, "Stk"},\n {"Milch", 500.0, "ml"}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_ingredient` vom Typ `PINGREDIENT` auf die zweite Zutat im Rezept. Verdoppeln Sie die Menge (`quantity`) dieser Zutat.',
solution: 'PINGREDIENT p_ingredient = &recipe_steps[1];\np_ingredient->quantity *= 2;',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct ingredient {\n char name[30];\n float quantity;\n char unit[10];\n} INGREDIENT, *PINGREDIENT;\n```\n- Die Struktur ist ideal, um zusammengehörige Daten für eine Zutat zu speichern. Der `typedef` wird um den Zeigertyp `PINGREDIENT` erweitert.\n\n**Teil b)**\n```c\nINGREDIENT recipe_steps[3] = {\n {"Mehl", 250.0, "g"},\n {"Eier", 2.0, "Stk"},\n {"Milch", 500.0, "ml"}\n};\n```\n- Das Array stellt eine Liste von Zutaten dar.\n\n**Teil c)**\n```c\nPINGREDIENT p_ingredient = &recipe_steps[1];\np_ingredient->quantity *= 2;\n```\n- Der Zeiger `p_ingredient` wird mit dem Typ `PINGREDIENT` deklariert und zeigt auf die zweite Zutat (Index 1).\n- `*=` ist der Multiplikationszuweisungsoperator. `p_ingredient->quantity *= 2;` ist eine Kurzform für `p_ingredient->quantity = p_ingredient->quantity * 2;`.'
},
{
id: 'ex10',
title: 'Aufgabe: Fahrzeugdaten',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `CAR` und einen Zeigertyp `PCAR` für eine anonyme Struktur mit:\n- `make` (max. 20)\n- `model` (max. 20)\n- `year` (int)\n- `mileage` (unsigned long)',
solution: 'typedef struct {\n char make[20];\n char model[20];\n int year;\n unsigned long mileage;\n} CAR, *PCAR;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein `CAR`-Array `fleet` mit 2 Fahrzeugen und initialisieren Sie es.',
solution: 'CAR fleet[2] = {\n {"Volkswagen", "Golf", 2020, 50000},\n {"BMW", "3er", 2021, 25000}\n};',
points: 3,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_car` vom Typ `PCAR`, der auf das zweite Auto in der Flotte zeigt. Geben Sie den Kilometerstand (`mileage`) dieses Autos aus.',
solution: 'PCAR p_car = &fleet[1];\nprintf("Kilometerstand: %lu", p_car->mileage);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n char make[20];\n char model[20];\n int year;\n unsigned long mileage;\n} CAR, *PCAR;\n```\n- `typedef ... CAR, *PCAR;` ist eine kompakte Methode, um sowohl den Strukturtyp `CAR` als auch den Zeigertyp `PCAR` (äquivalent zu `CAR*`) zu definieren.\n\n**Teil b)**\n```c\nCAR fleet[2] = {\n {"Volkswagen", "Golf", 2020, 50000},\n {"BMW", "3er", 2021, 25000}\n};\n```\n- Standardmäßige Initialisierung eines Arrays von Strukturen.\n\n**Teil c)**\n```c\nPCAR p_car = &fleet[1];\nprintf("Kilometerstand: %lu", p_car->mileage);\n```\n- Der Zeiger `p_car` wird mit dem vordefinierten Zeigertyp `PCAR` deklariert.\n- Der Format-Specifier für `unsigned long` ist `%lu`.'
},
{
id: 'ex11',
title: 'Aufgabe: GPS-Koordinate',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen `typedef` `GPS_COORD` und einen Zeigertyp `PGPS_COORD` für eine Struktur, die `latitude` und `longitude` (beides `double`) speichert.',
solution: 'typedef struct {\n double latitude;\n double longitude;\n} GPS_COORD, *PGPS_COORD;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `path` mit 2 Koordinaten und initialisieren Sie es mit Werten für z.B. Start und Ziel.',
solution: 'GPS_COORD path[2] = {\n {48.137154, 11.576124},\n {52.520008, 13.404954}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_coord` vom Typ `PGPS_COORD`, der auf den Anfang des `path`-Arrays zeigt. Geben Sie den Breitengrad (`latitude`) des Startpunktes über den Zeiger aus.',
solution: 'PGPS_COORD p_coord = path;\nprintf("Start Latitude: %f", p_coord->latitude);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n double latitude;\n double longitude;\n} GPS_COORD, *PGPS_COORD;\n```\n- Eine einfache Struktur zur Kapselung von zwei `double`-Werten. `PGPS_COORD` ist nun der Zeigertyp.\n\n**Teil b)**\n```c\nGPS_COORD path[2] = {\n {48.137154, 11.576124},\n {52.520008, 13.404954}\n};\n```\n- Das `path`-Array speichert zwei GPS-Punkte, die eine Route definieren könnten.\n\n**Teil c)**\n```c\nPGPS_COORD p_coord = path;\nprintf("Start Latitude: %f", p_coord->latitude);\n```\n- Der Zeiger `p_coord` wird mit dem neuen Typ `PGPS_COORD` deklariert. Wenn ein Zeiger auf den Namen eines Arrays gesetzt wird, zeigt er automatisch auf das erste Element des Arrays (`&path[0]`).\n- `p_coord->latitude` greift auf das `latitude`-Element des ersten Strukturelements im Array zu.'
},
{
id: 'ex12',
title: 'Aufgabe: Spielfigur-Werte',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `STATS` und einen Zeigertyp `PSTATS` für eine Struktur mit den Werten `health`, `mana`, `strength` und `defense` (alles `unsigned int`).',
solution: 'typedef struct {\n unsigned int health;\n unsigned int mana;\n unsigned int strength;\n unsigned int defense;\n} STATS, *PSTATS;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie zwei Variablen `hero` und `villain` vom Typ `STATS` und initialisieren Sie sie mit passenden Werten.',
solution: 'STATS hero = {100, 50, 15, 10};\nSTATS villain = {120, 30, 20, 5};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_hero` vom Typ `PSTATS`, der auf die `hero`-Struktur zeigt. Reduzieren Sie die `health` des Helden um 25 Punkte.',
solution: 'PSTATS p_hero = &hero;\np_hero->health -= 25;',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n unsigned int health;\n unsigned int mana;\n unsigned int strength;\n unsigned int defense;\n} STATS, *PSTATS;\n```\n- `unsigned int` ist eine gute Wahl für Spielwerte, da sie typischerweise nicht negativ sind. Der Zeigertyp `PSTATS` wird mitdefiniert.\n\n**Teil b)**\n```c\nSTATS hero = {100, 50, 15, 10};\nSTATS villain = {120, 30, 20, 5};\n```\n- Zwei separate Strukturvariablen werden deklariert und initialisiert.\n\n**Teil c)**\n```c\nPSTATS p_hero = &hero;\np_hero->health -= 25;\n```\n- Der Zeiger `p_hero` wird mit dem Typ `PSTATS` deklariert und mit der Adresse der `hero`-Variable initialisiert.\n- `-=` ist der Subtraktionszuweisungsoperator und eine Kurzform für `p_hero->health = p_hero->health - 25`.'
},
{
id: 'ex13',
title: 'Aufgabe: RGB-Farbwerte',
parts: [
{
id: 'a',
prompt: 'Definieren Sie eine Struktur `color` mit den drei Komponenten `r`, `g`, und `b`. Verwenden Sie den Datentyp `unsigned char`, da die Werte von 0-255 reichen. Erstellen Sie einen `typedef` `COLOR`.',
solution: 'typedef struct color {\n unsigned char r;\n unsigned char g;\n unsigned char b;\n} COLOR;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `palette` mit 3 Farben und initialisieren Sie es mit den Werten für Rot (255,0,0), Grün (0,255,0) und Blau (0,0,255).',
solution: 'COLOR palette[3] = {\n {255, 0, 0},\n {0, 255, 0},\n {0, 0, 255}\n};',
points: 3,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Greifen Sie direkt (ohne Zeiger) auf die zweite Farbe in der `palette` zu und ändern Sie deren Rot-Wert (`r`) auf 128.',
solution: 'palette[1].r = 128;',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct color {\n unsigned char r;\n unsigned char g;\n unsigned char b;\n} COLOR;\n```\n- `unsigned char` ist der optimale Datentyp für RGB-Werte, da er genau den Bereich von 0 bis 255 abdeckt und nur 1 Byte pro Komponente belegt.\n\n**Teil b)**\n```c\nCOLOR palette[3] = {\n {255, 0, 0},\n {0, 255, 0},\n {0, 0, 255}\n};\n```\n- Das Array `palette` speichert die drei Grundfarben.\n\n**Teil c)**\n```c\npalette[1].r = 128;\n```\n- Dies zeigt den direkten Zugriff auf ein Strukturelement innerhalb eines Arrays. `palette[1]` wählt das zweite Element (die grüne Farbe) aus, und `.r` greift auf dessen Rot-Komponente zu, die dann geändert wird. Es entsteht ein dunkleres Grün.'
},
{
id: 'ex14',
title: 'Aufgabe: Bankkonto',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen `typedef` `ACCOUNT`, sowie einen Zeigertyp `PACCOUNT`, für eine Struktur mit `account_number` (long long), `owner_name` (max. 40 Zeichen) und `balance` (double).',
solution: 'typedef struct {\n long long account_number;\n char owner_name[40];\n double balance;\n} ACCOUNT, *PACCOUNT;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie eine Variable `customer_account` vom Typ `ACCOUNT` und initialisieren Sie sie mit fiktiven Daten.',
solution: 'ACCOUNT customer_account = {123456789012, "Jane Smith", 1500.75};',
points: 3,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_acc` vom Typ `PACCOUNT` auf `customer_account`. Addieren Sie 500.25 zum Kontostand (`balance`) über den Zeiger.',
solution: 'PACCOUNT p_acc = &customer_account;\np_acc->balance += 500.25;',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n long long account_number;\n char owner_name[40];\n double balance;\n} ACCOUNT, *PACCOUNT;\n```\n- `long long` wird für die Kontonummer verwendet, um sicherzustellen, dass auch sehr lange Nummern Platz finden. `double` ist für Geldbeträge geeignet.\n- Der Zeigertyp `PACCOUNT` wird gleichzeitig mitdefiniert.\n\n**Teil b)**\n```c\nACCOUNT customer_account = {123456789012, "Jane Smith", 1500.75};\n```\n- Eine einzelne Strukturvariable wird mit den Kontodaten initialisiert.\n\n**Teil c)**\n```c\nPACCOUNT p_acc = &customer_account;\np_acc->balance += 500.25;\n```\n- Der Zeiger `p_acc` wird mit dem Typ `PACCOUNT` deklariert und zeigt auf das Kundenkonto. Der Additionszuweisungsoperator `+=` wird verwendet, um den Kontostand zu erhöhen.'
},
{
id: 'ex15',
title: 'Aufgabe: Kalendertermin (verschachtelt)',
parts: [
{
id: 'a',
prompt: 'Definieren Sie eine Struktur `date` mit `day`, `month`, `year` (alle `int`). Definieren Sie dann eine Struktur `appointment`, die `description` (max. 100) und eine `date`-Struktur namens `when` enthält. Erstellen Sie `typedef`s `APPOINTMENT` und `PAPPOINTMENT`.',
solution: 'struct date {\n int day;\n int month;\n int year;\n};\ntypedef struct appointment {\n char description[100];\n struct date when;\n} APPOINTMENT, *PAPPOINTMENT;',
points: 4,
type: 'struct-definition',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein `APPOINTMENT`-Array `calendar` mit 2 Terminen und initialisieren Sie es.',
solution: 'APPOINTMENT calendar[2] = {\n {"Zahnarzt", {15, 10, 2024}},\n {"Projekt-Deadline", {31, 10, 2024}}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_appt` vom Typ `PAPPOINTMENT`, der auf den ersten Termin im Kalender zeigt. Geben Sie das Jahr dieses Termins mit `printf` aus.',
solution: 'PAPPOINTMENT p_appt = calendar;\nprintf("Jahr des Termins: %d", p_appt->when.year);',
points: 2,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\nstruct date {\n int day;\n int month;\n int year;\n};\ntypedef struct appointment {\n char description[100];\n struct date when;\n} APPOINTMENT, *PAPPOINTMENT;\n```\n- Ein gutes Beispiel für Wiederverwendbarkeit: Die `date`-Struktur könnte auch in anderen Teilen des Programms genutzt werden. Der `typedef` für `appointment` schließt den Zeigertyp `PAPPOINTMENT` mit ein.\n\n**Teil b)**\n```c\nAPPOINTMENT calendar[2] = {\n {"Zahnarzt", {15, 10, 2024}},\n {"Projekt-Deadline", {31, 10, 2024}}\n};\n```\n- Die Initialisierung `{15, 10, 2024}` gehört zur inneren `when`-Struktur.\n\n**Teil c)**\n```c\nPAPPOINTMENT p_appt = calendar;\nprintf("Jahr des Termins: %d", p_appt->when.year);\n```\n- Der Zeiger `p_appt` wird mit dem Typ `PAPPOINTMENT` deklariert.\n- `p_appt->when` greift auf die `date`-Struktur zu. Von dort aus wird `.year` verwendet, um auf das Jahr zuzugreifen.'
},
{
id: 'ex16',
title: 'Aufgabe: Messwert',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen `typedef` `MEASUREMENT` für eine Struktur mit `value` (double) und `unit` (max. 15 Zeichen).',
solution: 'typedef struct {\n double value;\n char unit[15];\n} MEASUREMENT;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `sensor_data` für 3 Messwerte und initialisieren Sie es (z.B. Temperatur, Luftdruck, Feuchtigkeit).',
solution: 'MEASUREMENT sensor_data[3] = {\n {23.5, "Celsius"},\n {1013.25, "hPa"},\n {45.5, "Percent"}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Geben Sie den `value` des dritten Messwerts (Index 2) direkt (ohne Zeiger) mit `printf` aus.',
solution: 'printf("Wert: %f %s", sensor_data[2].value, sensor_data[2].unit);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n double value;\n char unit[15];\n} MEASUREMENT;\n```\n- Diese Struktur ist nützlich, um Messwerte zusammen mit ihren Einheiten zu speichern, was die Daten aussagekräftiger macht.\n\n**Teil b)**\n```c\nMEASUREMENT sensor_data[3] = {\n {23.5, "Celsius"},\n {1013.25, "hPa"},\n {45.5, "Percent"}\n};\n```\n- Ein Array, das eine Reihe von Messungen von verschiedenen Sensoren darstellen könnte.\n\n**Teil c)**\n```c\nprintf("Wert: %f %s", sensor_data[2].value, sensor_data[2].unit);\n```\n- Dies zeigt den direkten Zugriff auf ein Element in einem Array von Strukturen mit `array[index].member`.\n- `%f` wird für den `double`-Wert und `%s` für die `unit`-Zeichenkette verwendet.'
},
{
id: 'ex17',
title: 'Aufgabe: Kontaktinformationen',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen Datentyp `CONTACT` für eine Struktur mit `name` (max. 50), `phone` (max. 20) und `email` (max. 50).',
solution: 'typedef struct {\n char name[50];\n char phone[20];\n char email[50];\n} CONTACT;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `address_book` mit 2 Kontakten und initialisieren Sie es.',
solution: 'CONTACT address_book[2] = {\n {"Alice", "123-456-7890", "alice@example.com"},\n {"Bob", "098-765-4321", "bob@example.com"}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_contact`, der auf den ersten Kontakt zeigt. Ändern Sie dessen Telefonnummer auf "555-555-5555". Beachten Sie: Zeichenketten können nicht direkt zugewiesen werden. (Die Lösung verwendet `strcpy`, aber für diese Übung reicht es, dies konzeptionell zu beschreiben - wir gehen davon aus, die Zuweisung wäre einfach.)',
solution: 'CONTACT *p_contact = address_book;\n// In echtem C wäre strcpy(p_contact->phone, "555-555-5555"); nötig.\n// Für diese Übung akzeptieren wir eine konzeptionelle Zuweisung.',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n char name[50];\n char phone[20];\n char email[50];\n} CONTACT;\n```\n- Eine Standardstruktur für ein Adressbuch.\n\n**Teil b)**\n```c\nCONTACT address_book[2] = {\n {"Alice", "123-456-7890", "alice@example.com"},\n {"Bob", "098-765-4321", "bob@example.com"}\n};\n```\n- Initialisierung des Adressbuch-Arrays.\n\n**Teil c)**\n```c\n#include <string.h> // Erforderlich für strcpy\nCONTACT *p_contact = address_book;\nstrcpy(p_contact->phone, "555-555-5555");\n```\n- `p_contact` zeigt auf den ersten Kontakt.\n- **Wichtiger Hinweis zu C:** Zeichenketten (char-Arrays) können nicht mit `=` zugewiesen werden. Man muss die Funktion `strcpy()` aus `<string.h>` verwenden, um den Inhalt einer Zeichenkette in eine andere zu kopieren. Die einfache Zuweisung würde nur den Zeiger kopieren, nicht den Inhalt, was hier nicht funktioniert.'
},
{
id: 'ex18',
title: 'Aufgabe: Datei-Metadaten',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen `typedef` `FILE_META` für eine Struktur mit `filename` (max. 256), `size_kb` (unsigned long) und `permissions` (char-Array der Größe 11, z.B. "-rwxr-xr--").',
solution: 'typedef struct {\n char filename[256];\n unsigned long size_kb;\n char permissions[11];\n} FILE_META;',
points: 4,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren und initialisieren Sie eine `FILE_META`-Variable `my_file`.',
solution: 'FILE_META my_file = {"document.txt", 128, "-rw-r--r--"};',
points: 3,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_file` auf `my_file`. Geben Sie Dateinamen und Größe über den Zeiger aus.',
solution: 'FILE_META *p_file = &my_file;\nprintf("File: %s, Size: %lu KB", p_file->filename, p_file->size_kb);',
points: 3,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n char filename[256];\n unsigned long size_kb;\n char permissions[11];\n} FILE_META;\n```\n- Diese Struktur modelliert die Art von Metadaten, die ein Dateisystem speichert. `permissions` ist ein String fester Länge.\n\n**Teil b)**\n```c\nFILE_META my_file = {"document.txt", 128, "-rw-r--r--"};\n```\n- Initialisierung einer einzelnen Strukturvariable.\n\n**Teil c)**\n```c\nFILE_META *p_file = &my_file;\nprintf("File: %s, Size: %lu KB", p_file->filename, p_file->size_kb);\n```\n- Der Zeiger `p_file` wird verwendet, um auf die Member der `my_file`-Struktur zuzugreifen und sie auszugeben.'
},
{
id: 'ex19',
title: 'Aufgabe: Rechteck (verschachtelt)',
parts: [
{
id: 'a',
prompt: 'Definieren Sie eine Struktur `point` mit `x` und `y` (beides `int`). Definieren Sie dann eine Struktur `rectangle`, die zwei `point`-Strukturen enthält: `top_left` und `bottom_right`. Erstellen Sie `typedef`s `POINT` und `RECTANGLE`.',
solution: 'typedef struct {\n int x;\n int y;\n} POINT;\ntypedef struct {\n POINT top_left;\n POINT bottom_right;\n} RECTANGLE;',
points: 4,
type: 'struct-definition',
},
{
id: 'b',
prompt: 'Deklarieren Sie eine `RECTANGLE`-Variable `selection` und initialisieren Sie sie so, dass sie den Bereich von (10, 50) bis (100, 200) abdeckt.',
solution: 'RECTANGLE selection = {{10, 50}, {100, 200}};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Greifen Sie direkt (ohne Zeiger) auf die `y`-Koordinate des `bottom_right`-Punktes der `selection` zu und geben Sie sie aus.',
solution: 'printf("Untere Y-Koordinate: %d", selection.bottom_right.y);',
points: 2,
type: 'printf',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n int x;\n int y;\n} POINT;\ntypedef struct {\n POINT top_left;\n POINT bottom_right;\n} RECTANGLE;\n```\n- Hier wird eine Struktur (`RECTANGLE`) definiert, die selbst zwei Instanzen einer anderen Struktur (`POINT`) enthält. Dies ist eine sehr häufige und nützliche Technik.\n\n**Teil b)**\n```c\nRECTANGLE selection = {{10, 50}, {100, 200}};\n```\n- Die verschachtelten Klammern sind wichtig. Das äußere Paar ist für die `RECTANGLE`-Struktur. Jedes innere Paar ist für eine der `POINT`-Strukturen.\n\n**Teil c)**\n```c\nprintf("Untere Y-Koordinate: %d", selection.bottom_right.y);\n```\n- Der Zugriff auf tief verschachtelte Elemente erfolgt durch Aneinanderreihung des Punkt-Operators: `variable.outer_member.inner_member`.'
},
{
id: 'ex20',
title: 'Aufgabe: Wählerverzeichnis',
parts: [
{
id: 'a',
prompt: 'Definieren Sie einen `typedef` `VOTER` und einen Zeigertyp `PVOTER` für eine Struktur mit `voter_id` (int), `name` (max. 60) und `has_voted` (int, wobei 0=nein, 1=ja).',
solution: 'typedef struct {\n int voter_id;\n char name[60];\n int has_voted;\n} VOTER, *PVOTER;',
points: 3,
type: 'typedef-struct',
},
{
id: 'b',
prompt: 'Deklarieren Sie ein Array `voter_list` mit 2 Wählern und initialisieren Sie es. Der erste hat bereits gewählt, der zweite nicht.',
solution: 'VOTER voter_list[2] = {\n {101, "Peter Pan", 1},\n {102, "Wendy Darling", 0}\n};',
points: 4,
type: 'array-initialization',
},
{
id: 'c',
prompt: 'Deklarieren Sie einen Zeiger `p_voter` vom Typ `PVOTER`, der auf den zweiten Wähler zeigt. Setzen Sie dessen `has_voted`-Status auf 1.',
solution: 'PVOTER p_voter = &voter_list[1];\np_voter->has_voted = 1;',
points: 3,
type: 'pointer-manipulation',
},
],
explanation: '### Lösungserklärung\n\n**Teil a)**\n```c\ntypedef struct {\n int voter_id;\n char name[60];\n int has_voted;\n} VOTER, *PVOTER;\n```\n- `has_voted` als `int` zu verwenden ist eine gängige Praxis in C, um einen booleschen Wert darzustellen. Der Zeigertyp `PVOTER` wird mitdefiniert.\n\n**Teil b)**\n```c\nVOTER voter_list[2] = {\n {101, "Peter Pan", 1},\n {102, "Wendy Darling", 0}\n};\n```\n- Das Array wird mit den Daten der beiden Wähler initialisiert.\n\n**Teil c)**\n```c\nPVOTER p_voter = &voter_list[1];\np_voter->has_voted = 1;\n```\n- Der Zeiger `p_voter` wird mit dem Typ `PVOTER` auf das zweite Element des Arrays gesetzt.\n- `p_voter->has_voted = 1;` aktualisiert den Status des Wählers, um zu markieren, dass er gewählt hat.'
}
];

View File

@ -0,0 +1,285 @@
import { Exercise } from '../types';
export const programmingExercises: Exercise[] = [
{
id: 'prog-ex1',
title: 'Programmieren: Zahlenraten',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `GenZufall(void)` mit Rückgabetyp `int`, welche mithilfe der Funktion `rand()` aus `<stdlib.h>` eine zufällige Zahl zwischen 1 und 128 generiert und zurückgibt.',
solution: 'int GenZufall(void) {\n return (rand() % 128) + 1;\n}',
points: 5,
type: 'function-definition',
},
{
id: 'b',
prompt: 'Erstellen Sie eine C-Funktion `PruefeZahl` mit Rückgabetyp `int` und zwei Parametern `int ziel`, `int rat`. Die Funktion soll die beiden Zahlen vergleichen:\n- wenn `rat > ziel`: Ausgabe "Zahl zu gross!" und Rückgabewert 1\n- wenn `rat < ziel`: Ausgabe "Zahl zu klein!" und Rückgabewert -1\n- wenn `rat = ziel`: keine Ausgabe und Rückgabewert 0',
solution: 'int PruefeZahl(int ziel, int rat) {\n if (rat > ziel) {\n printf("Zahl zu gross!\\n");\n return 1;\n } else if (rat < ziel) {\n printf("Zahl zu klein!\\n");\n return -1;\n } else {\n return 0;\n }\n}',
points: 7,
type: 'function-definition',
},
{
id: 'c',
prompt: 'Erstellen Sie nun ein C-Programm inkl. `main`. Generieren Sie eine Zufallszahl `ziel`. In einer Schleife wird vom Benutzer die Zahl `rat` eingelesen und mit `PruefeZahl()` verglichen. Die Eingabe wird wiederholt, bis die richtige Zahl erraten wurde oder 7 Versuche erreicht sind. Geben Sie am Ende das Ergebnis aus: "Erraten in %i Versuchen!" oder "Zu viele Versuche. Die gesuchte Zahl war %i."',
solution: '#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\nint GenZufall(void);\nint PruefeZahl(int ziel, int rat);\n\nint main() {\n int ziel, rat, erg, vers = 0;\n srand(time(NULL));\n ziel = GenZufall();\n\n printf("Ich habe eine Zahl zwischen 1 und 128. Du hast 7 Versuche.\\n");\n\n for (vers = 1; vers <= 7; vers++) {\n printf("Versuch %d: Gib deine Zahl ein: ", vers);\n scanf("%d", &rat);\n erg = PruefeZahl(ziel, rat);\n if (erg == 0) {\n break;\n }\n }\n\n if (erg == 0) {\n printf("Erraten in %d Versuchen!\\n", vers);\n } else {\n printf("Zu viele Versuche. Die gesuchte Zahl war %d.\\n", ziel);\n }\n\n return 0;\n}',
points: 12,
type: 'main-function',
},
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert ein einfaches Zahlenratespiel.\n\n**Teil a)**\n```c\nint GenZufall(void) {\n // rand() % 128 erzeugt eine Zahl von 0-127.\n // +1 verschiebt den Bereich auf 1-128.\n return (rand() % 128) + 1;\n}\n```\nDie Funktion `GenZufall` nutzt den Modulo-Operator, um den riesigen Zahlenbereich von `rand()` auf den gewünschten Bereich zu begrenzen.\n\n**Teil b)**\n```c\nint PruefeZahl(int ziel, int rat) {\n if (rat > ziel) {\n printf("Zahl zu gross!\\n");\n return 1;\n } else if (rat < ziel) {\n printf("Zahl zu klein!\\n");\n return -1;\n } else {\n return 0; // Zahlen sind gleich\n }\n}\n```\n`PruefeZahl` kapselt die Vergleichslogik und gibt je nach Ergebnis einen unterschiedlichen Wert zurück. Dies macht den Code in `main` sauberer und besser lesbar.\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <stdlib.h>\n#include <time.h>\n\n// Prototypen\nint GenZufall(void);\nint PruefeZahl(int ziel, int rat);\n\nint main() {\n int ziel, rat, erg, vers = 0;\n // Zufallsgenerator initialisieren\n srand(time(NULL));\n ziel = GenZufall();\n\n for (vers = 1; vers <= 7; vers++) {\n printf("Versuch %d: Gib deine Zahl ein: ", vers);\n scanf("%d", &rat);\n erg = PruefeZahl(ziel, rat);\n if (erg == 0) {\n break; // Schleife verlassen bei Erfolg\n }\n }\n\n if (erg == 0) {\n printf("Erraten in %d Versuchen!\\n", vers);\n } else {\n printf("Zu viele Versuche. Die gesuchte Zahl war %d.\\n", ziel);\n }\n return 0;\n}\n```\nDie `main`-Funktion steuert den Spielfluss. `srand(time(NULL))` ist wichtig, damit bei jedem Programmlauf neue Zufallszahlen erzeugt werden. Die `for`-Schleife begrenzt die Anzahl der Versuche. Ein `break` beendet die Schleife vorzeitig, wenn die richtige Zahl geraten wurde.'
},
{
id: 'prog-ex2',
title: 'Programmieren: Vokaleraten (Glücksrad)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `ErsetzeVokal(char ch, char newch)` mit Rückgabetyp `char`. Sie soll prüfen, ob `ch` ein Vokal ist (Groß- & Kleinschreibung). Wenn ja, wird `newch` zurückgegeben, sonst `ch`.',
solution: 'char ErsetzeVokal(char ch, char newch) {\n switch(ch) {\n case \'a\':\n case \'e\':\n case \'i\':\n case \'o\':\n case \'u\':\n case \'A\':\n case \'E\':\n case \'I\':\n case \'O\':\n case \'U\':\n return newch;\n default:\n return ch;\n }\n}',
points: 6,
type: 'function-definition',
},
{
id: 'b',
prompt: 'Erstellen Sie eine C-Funktion `ErzeugeLuecken(char text[])` ohne Rückgabewert. Sie soll den String `text` durchlaufen und jedes Zeichen mit `ErsetzeVokal()` prüfen, um Vokale durch einen Unterstrich `_` zu ersetzen.',
solution: 'void ErzeugeLuecken(char text[]) {\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = ErsetzeVokal(text[i], \'_\');\n }\n}',
points: 6,
type: 'function-definition',
},
{
id: 'c',
prompt: 'Erstellen Sie ein `main`-Programm, das einen Text (max. 30 Zeichen) einliest. Rufen Sie `ErzeugeLuecken()` auf, um die Vokale zu ersetzen. Geben Sie zum Schluss den modifizierten Text aus.\n\n**Hinweis:** Nutzen Sie `fgets(buffer, size, stdin)` für eine sichere Eingabe. Denken Sie daran, dass `fgets` auch das Zeilenumbruch-Zeichen (`\\n`) mit einliest, das Sie eventuell entfernen müssen.',
solution: '#include <stdio.h>\n#include <string.h>\n\nchar ErsetzeVokal(char ch, char newch);\nvoid ErzeugeLuecken(char text[]);\n\nint main() {\n char text[32]; // 30 chars + newline + null\n printf("Bitte Text eingeben (max. 30 char): ");\n fgets(text, sizeof(text), stdin);\n\n // Remove newline character if present\n text[strcspn(text, "\\n")] = 0;\n\n ErzeugeLuecken(text);\n printf("Text ohne Vokale: %s\\n", text);\n\n return 0;\n}',
points: 12,
type: 'main-function',
},
],
explanation: '### Lösungserklärung\n\nDieses Programm wandelt einen Text so um, wie man es vom "Glücksrad" kennt.\n\n**Teil a)**\n```c\nchar ErsetzeVokal(char ch, char newch) {\n // Die switch-Anweisung prüft den Buchstaben.\n // Bei Vokalen wird newch zurückgegeben.\n switch(ch) {\n case \'a\':\n case \'e\':\n case \'i\':\n case \'o\':\n case \'u\':\n case \'A\':\n case \'E\':\n case \'I\':\n case \'O\':\n case \'U\':\n return newch;\n // default wird ausgeführt, wenn keiner der cases zutrifft.\n default:\n return ch;\n }\n}\n```\n`ErsetzeVokal` nutzt ein `switch`-Statement mit "fall-through", um effizient zu prüfen, ob ein Zeichen ein Vokal ist. Dies ist übersichtlicher als eine lange `if-else-if`-Kette.\n\n**Teil b)**\n```c\nvoid ErzeugeLuecken(char text[]) {\n // Schleife läuft, bis das String-Ende-Zeichen \'\\0\' erreicht ist.\n for (int i = 0; text[i] != \'\\0\'; i++) {\n // Das Zeichen im Text wird direkt überschrieben.\n text[i] = ErsetzeVokal(text[i], \'_\');\n }\n}\n```\n`ErzeugeLuecken` iteriert mit einer `for`-Schleife durch den String und wendet die Logik aus Teil a) auf jedes Zeichen an. Da Strings in C (char-Arrays) direkt modifiziert werden können, ist kein Rückgabewert nötig (call by reference).\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <string.h>\n\n// Prototypen\nchar ErsetzeVokal(char ch, char newch);\nvoid ErzeugeLuecken(char text[]);\n\nint main() {\n // Puffer für 30 Zeichen + Newline + Nullterminator\n char text[32]; \n printf("Bitte Text eingeben (max. 30 char): ");\n fgets(text, sizeof(text), stdin);\n\n // Entfernt das Newline-Zeichen, das fgets mit einliest.\n text[strcspn(text, "\\n")] = 0;\n\n ErzeugeLuecken(text);\n printf("Text ohne Vokale: %s\\n", text);\n\n return 0;\n}\n```\nDie `main`-Funktion liest einen String sicher mit `fgets` ein, um Buffer Overflows zu vermeiden. `strcspn` ist eine nützliche Funktion aus `string.h`, um das oft störende Newline-Zeichen am Ende des `fgets`-Inputs zu finden und zu entfernen.'
},
{
id: 'prog-ex3',
title: 'Programmieren: Hangman',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine C-Funktion `CheckWon(int remain, int lives)`, die prüft, ob gewonnen oder verloren wurde. Wenn `remain == 0` und `lives > 0`, gib "Gewonnen" aus, ansonsten "Leider verloren".',
solution: 'void CheckWon(int remain, int lives) {\n if (remain == 0 && lives > 0) {\n printf("Gewonnen\\n");\n } else {\n printf("Leider verloren\\n");\n }\n}',
points: 4,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Erstellen Sie eine Funktion `TestChar(char word[], char quest[], int len, char c, int *remain)` mit `int`-Rückgabe. Sie durchläuft `word`. Wenn `word[i] == c` und `quest[i] == \'-\'`, wird `quest[i]` auf `c` gesetzt, `*remain` dekrementiert und `found` auf 1 gesetzt. Geben Sie am Ende `found` zurück.',
solution: 'int TestChar(char word[], char quest[], int len, char c, int *remain) {\n int found = 0;\n for (int i = 0; i < len; i++) {\n if (word[i] == c && quest[i] == \'-\') {\n quest[i] = c;\n (*remain)--;\n found = 1;\n }\n }\n return found;\n}',
points: 7,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie die `main`-Funktion für Hangman. Wort einlesen (max 10 Zeichen), Länge `len` ermitteln. Ratewort `quest` mit Bindestrichen füllen. In einer Schleife (solange `lives > 0` und `remain > 0`): Leben ausgeben, Buchstabe `c` einlesen, `TestChar()` aufrufen. Wenn `TestChar` 0 zurückgibt, `lives` dekrementieren. `quest` ausgeben. Am Ende `CheckWon()` aufrufen.\n\n**Hinweise:** Nutzen Sie `strlen()` aus `<string.h>` für die Länge. Beim Einlesen eines einzelnen Zeichens mit `scanf()` verwenden Sie am besten `scanf(" %c", &c)` (mit Leerzeichen davor), um Zeilenumbrüche aus der vorherigen Eingabe zu ignorieren.',
solution: '#include <stdio.h>\n#include <string.h>\n\nvoid CheckWon(int remain, int lives);\nint TestChar(char word[], char quest[], int len, char c, int *remain);\n\nint main(void) {\n char word[11];\n char quest[11];\n int len = 0;\n char c = \'\\0\';\n int remain = 0;\n int lives = 5;\n\n printf("Geben Sie das zu suchende Wort ein (max. 10 Buchstaben): ");\n scanf("%10s", word);\n len = strlen(word);\n remain = len;\n\n for (int i = 0; i < len; i++) {\n quest[i] = \'-\';\n }\n quest[len] = \'\\0\';\n\n printf("Das Wort hat %d Buchstaben: %s\\n", len, quest);\n\n while (lives > 0 && remain > 0) {\n printf("Leben: %d. Buchstabe eingeben: ", lives);\n scanf(" %c", &c); // Leerzeichen vor %c um Whitespace zu konsumieren\n\n if (TestChar(word, quest, len, c, &remain) == 0) {\n lives--;\n }\n printf("%s\\n", quest);\n }\n\n CheckWon(remain, lives);\n\n return 0;\n}',
points: 13,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm ist eine einfache Implementierung des Spiels "Hangman".\n\n**Teil a)**\n```c\nvoid CheckWon(int remain, int lives) {\n if (remain == 0 && lives > 0) {\n printf("Gewonnen\\n");\n } else {\n printf("Leider verloren\\n");\n }\n}\n```\n`CheckWon` ist eine simple Funktion, die nur das Spielende basierend auf den finalen Werten für `remain` (übrige Buchstaben) und `lives` (übrige Leben) auswertet.\n\n**Teil b)**\n```c\nint TestChar(char word[], char quest[], int len, char c, int *remain) {\n int found = 0;\n for (int i = 0; i < len; i++) {\n // Prüfe auf Übereinstimmung UND ob der Buchstabe noch nicht gefunden wurde.\n if (word[i] == c && quest[i] == \'-\') {\n quest[i] = c;\n (*remain)--; // Dereferenzierung des Zeigers, um den Wert in main zu ändern.\n found = 1;\n }\n }\n return found;\n}\n```\n`TestChar` ist die Kernlogik des Spiels. Sie prüft nicht nur, ob ein Buchstabe im Wort vorkommt, sondern auch, ob er nicht bereits aufgedeckt wurde (`quest[i] == \'-\'`). Die Verwendung eines Zeigers auf `remain` (`int *remain`) erlaubt es der Funktion, eine Variable in `main` direkt zu modifizieren (`call by reference`).\n\n**Teil c)**\n```c\n#include <stdio.h>\n#include <string.h>\n\n// Prototypen\nvoid CheckWon(int remain, int lives);\nint TestChar(char word[], char quest[], int len, char c, int *remain);\n\nint main(void) {\n char word[11], quest[11];\n int len = 0, remain = 0, lives = 5;\n char c = \'\\0\';\n\n scanf("%10s", word);\n len = strlen(word);\n remain = len;\n\n for (int i = 0; i < len; i++) quest[i] = \'-\';\n quest[len] = \'\\0\';\n\n while (lives > 0 && remain > 0) {\n printf("%s\\nLeben: %d. Buchstabe: ", quest, lives);\n scanf(" %c", &c); // Leerzeichen vor %c konsumiert Whitespace (z.B. Enter)\n\n if (TestChar(word, quest, len, c, &remain) == 0) {\n lives--;\n }\n }\n\n CheckWon(remain, lives);\n return 0;\n}\n```\nDie `main`-Funktion initialisiert das Spiel, indem sie das Ratewort `quest` mit Bindestrichen füllt. Die `while`-Schleife läuft, solange das Spiel aktiv ist. Am Ende wird `CheckWon` aufgerufen, um das Ergebnis anzuzeigen. Das Leerzeichen in `scanf(" %c", &c)` ist ein wichtiger Trick, um übrig gebliebene Newline-Zeichen aus vorherigen Eingaben zu ignorieren.'
},
{
id: 'prog-ex4',
title: 'Programmieren: Chatbot-Wortvergleich',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie die C-Funktion `wortvergleich(char wort_1[], char wort_2[])` mit Rückgabetyp `int`. Die Funktion vergleicht die beiden Wörter und liefert 1 für gleiche Wörter, sonst 0. Verwenden Sie keine `string.h`-Funktionen.',
solution: 'int wortvergleich(char wort_1[], char wort_2[]) {\n int i = 0;\n while (wort_1[i] != \'\\0\' && wort_2[i] != \'\\0\') {\n if (wort_1[i] != wort_2[i]) {\n return 0; // Mismatch found\n }\n i++;\n }\n // If we are here, one string has ended.\n // They are equal only if both ended at the same time.\n if (wort_1[i] == \'\\0\' && wort_2[i] == \'\\0\') {\n return 1;\n } \n return 0; // Different lengths\n}',
points: 9,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie die Funktion `wortholen(char satz[])`, die aus einem Satz ein Wort extrahiert und einen `char`-Pointer zurückgibt. Eine statische `int`-Variable `pos` merkt sich die Position. Bei jedem Aufruf wird das nächste Wort zurückgegeben. `wortholen("\\0")` setzt die Position zurück.',
solution: 'char* wortholen(char satz[]) {\n static int pos = 0;\n\n if (satz[0] == \'\\0\') { // Reset condition\n pos = 0;\n return "\\0";\n }\n \n char* wort = &satz[pos];\n if (wort[0] == \'\\0\') { // End of sentence\n pos = 0;\n return "\\0";\n }\n\n while(satz[pos] != \' \' && satz[pos] != \'\\n\' && satz[pos] != \'\\0\') {\n pos++;\n }\n\n if (satz[pos] != \'\\0\') {\n satz[pos] = \'\\0\'; // Terminate the word\n pos++;\n }\n \n return wort;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie die `main()`-Funktion, die einen Satz einliest. Verwenden Sie eine `do-while`-Schleife und `wortholen()`, um den Satz Wort für Wort zu durchlaufen. Vergleichen Sie jedes Wort mit einer vordefinierten `wortliste` via `wortvergleich()`. Bei einer Übereinstimmung, geben Sie die passende Antwort aus `antworten` aus.',
solution: '#include <stdio.h>\n#define WORTLAENGE 20\n#define SATZLAENGE 100\n#define ANZAHL 5\n\nchar wortliste[ANZAHL][WORTLAENGE] = {\n"nicht", "du", "denke", "hasse", "was"};\nchar antworten[ANZAHL][SATZLAENGE] = {\n"sprich am besten davon!",\n"wir sprechen nicht von mir!",\n"warum denkst du das?",\n"du bist sauer - sag mehr dazu!",\n"warum fragst du?"};\n\nint wortvergleich(char wort_1[], char wort_2[]);\nchar* wortholen(char satz[]);\n\nint main() {\n char satz[SATZLAENGE];\n char* wort;\n int gefunden, i;\n\n printf("Hallo wie geht es dir heute?\\n");\n while(fgets(satz, SATZLAENGE, stdin) != NULL) {\n wortholen("\\0"); // Reset word extractor for new sentence\n gefunden = 0;\n do {\n wort = wortholen(satz);\n if (wort[0] == \'\\0\') break;\n\n for (i = 0; i < ANZAHL; i++) {\n if (wortvergleich(wort, wortliste[i])) {\n printf("-> %s\\n", antworten[i]);\n gefunden = 1;\n break;\n }\n }\n if(gefunden) break; // Exit do-while if answer is found\n\n } while (wort[0] != \'\\0\');\n\n if (!gefunden) {\n printf("-> Erzähl mir mehr.\\n");\n }\n printf("\\n");\n }\n return 0;\n}',
points: 7,
type: 'main-function'
},
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert einen einfachen Chatbot, der auf Schlüsselwörter in einem Satz reagiert.\n\n**Teil a)**\n```c\nint wortvergleich(char wort_1[], char wort_2[]) {\n int i = 0;\n // Laufe solange, wie beide Strings Zeichen haben\n while (wort_1[i] != \'\\0\' && wort_2[i] != \'\\0\') {\n if (wort_1[i] != wort_2[i]) {\n return 0; // Zeichen ungleich -> Wörter ungleich\n }\n i++;\n }\n // Wenn Schleife endet, sind die Wörter nur gleich,\n // wenn beide Strings an der gleichen Stelle enden.\n if (wort_1[i] == \'\\0\' && wort_2[i] == \'\\0\') {\n return 1;\n } \n return 0; // Längen sind unterschiedlich\n}\n```\n`wortvergleich` ist eine manuelle Implementierung von `strcmp`. Sie vergleicht zwei Strings Zeichen für Zeichen und stellt sicher, dass sie auch die gleiche Länge haben.\n\n**Teil b)**\n```c\nchar* wortholen(char satz[]) {\n static int pos = 0;\n\n if (satz[0] == \'\\0\') { // Reset-Bedingung\n pos = 0;\n return "\\0";\n }\n \n char* wort = &satz[pos];\n if (wort[0] == \'\\0\') { // Ende des Satzes erreicht\n pos = 0;\n return "\\0";\n }\n\n // Gehe zum Ende des aktuellen Wortes\n while(satz[pos] != \' \' && satz[pos] != \'\\n\' && satz[pos] != \'\\0\') {\n pos++;\n }\n\n // Wenn wir nicht am Ende des Satzes sind, Wort terminieren\n if (satz[pos] != \'\\0\') {\n satz[pos] = \'\\0\';\n pos++; // Gehe zum Anfang des nächsten Wortes\n }\n \n return wort; // Gib den Pointer auf den Wortanfang zurück\n}\n```\n`wortholen` ist ein "Tokenizer". Die `static`-Variable `pos` ist entscheidend, da sie ihren Wert zwischen Funktionsaufrufen behält und sich so die Position im Satz "merkt". Die Funktion modifiziert den Eingabestring, indem sie `\\0`-Zeichen setzt, um die extrahierten Wörter zu terminieren.\n\n**Teil c)**\n```c\n// Globale Variablen und Prototypen (wie in Aufgabe definiert)\n// ...\n\nint main() {\n char satz[SATZLAENGE];\n char* wort;\n int gefunden;\n\n printf("Hallo wie geht es dir heute?\\n");\n // Endlosschleife für den Chat\n while(fgets(satz, SATZLAENGE, stdin) != NULL) {\n wortholen("\\0"); // Position für neuen Satz zurücksetzen\n gefunden = 0;\n do {\n wort = wortholen(satz);\n if (wort[0] == \'\\0\') break; // Ende des Satzes\n\n for (int i = 0; i < ANZAHL; i++) {\n if (wortvergleich(wort, wortliste[i])) {\n printf("-> %s\\n", antworten[i]);\n gefunden = 1;\n break; // innere Schleife verlassen\n }\n }\n if(gefunden) break; // äußere Schleife verlassen\n\n } while (1); // Endlosschleife, wird durch breaks beendet\n\n if (!gefunden) {\n printf("-> Erzähl mir mehr.\\n");\n }\n }\n return 0;\n}\n```\nDie `main`-Funktion enthält die Hauptschleife. Pro Satz wird eine `do-while`-Schleife gestartet, die den Satz mit `wortholen` zerlegt und mit `wortvergleich` nach bekannten Wörtern sucht. Wenn ein Wort gefunden wird, werden beide Schleifen verlassen, um doppelte Antworten zu vermeiden.'
},
{
id: 'prog-ex5',
title: 'Programmieren: Caesar-Verschlüsselung',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `rotiere_buchstabe(char c, int shift)`, die einen Buchstaben `c` um `shift` Stellen im Alphabet verschiebt. Die Funktion soll nur auf Kleinbuchstaben (`a`-`z`) reagieren und am Ende des Alphabets wieder bei `a` anfangen (z.B. `z` mit `shift=2` wird zu `b`). Andere Zeichen sollen unverändert bleiben.',
solution: 'char rotiere_buchstabe(char c, int shift) {\n if (c >= \'a\' && c <= \'z\') {\n return \'a\' + (c - \'a\' + shift) % 26;\n }\n return c;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `verschluessle(char text[], int shift)`, die einen ganzen Text verschlüsselt, indem sie `rotiere_buchstabe()` auf jedes Zeichen des Textes anwendet.',
solution: 'void verschluessle(char text[], int shift) {\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = rotiere_buchstabe(text[i], shift);\n }\n}',
points: 6,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die einen Text und einen `shift`-Wert vom Benutzer einliest, die `verschluessle()`-Funktion aufruft und das Ergebnis ausgibt.',
solution: '#include <stdio.h>\n\nchar rotiere_buchstabe(char c, int shift);\nvoid verschluessle(char text[], int shift);\n\nint main() {\n char text[100];\n int shift;\n\n printf("Text zur Verschluesselung eingeben: ");\n fgets(text, 100, stdin);\n\n printf("Verschiebung eingeben (1-25): ");\n scanf("%d", &shift);\n\n verschluessle(text, shift);\n\n printf("Verschluesselter Text: %s", text);\n\n return 0;\n}',
points: 10,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert die Caesar-Verschlüsselung, eine einfache Form der Zeichen-Substitution.\n\n**Teil a)**\n```c\nchar rotiere_buchstabe(char c, int shift) {\n // Prüfen, ob das Zeichen ein Kleinbuchstabe ist.\n if (c >= \'a\' && c <= \'z\') {\n // Den Buchstaben auf einen Wert 0-25 bringen (a=0, b=1, ...)\n // Shift addieren und Modulo 26 rechnen für den Überlauf.\n // Wieder \'a\' addieren, um zurück in den ASCII-Bereich zu kommen.\n return \'a\' + (c - \'a\' + shift) % 26;\n }\n // Andere Zeichen unverändert zurückgeben.\n return c;\n}\n```\nDie Kernlogik liegt in der Modulo-Arithmetik. `(c - \'a\')` normalisiert den Buchstaben auf einen Index von 0-25. Nach Addition des `shift`-Wertes sorgt `% 26` dafür, dass das Ergebnis im Bereich bleibt und bei `z` wieder zu `a` umbricht.\n\n**Teil b)**\n```c\nvoid verschluessle(char text[], int shift) {\n // Iteriere durch den String bis zum Nullterminator.\n for (int i = 0; text[i] != \'\\0\'; i++) {\n text[i] = rotiere_buchstabe(text[i], shift);\n }\n}\n```\nDiese Funktion wendet die Logik aus Teil a) auf einen kompletten String an. Sie modifiziert den String direkt (in-place), da Arrays in C als Referenz übergeben werden.\n\n**Teil c)**\n```c\n#include <stdio.h>\n\n// Prototypen\nchar rotiere_buchstabe(char c, int shift);\nvoid verschluessle(char text[], int shift);\n\nint main() {\n char text[100];\n int shift;\n\n printf("Text eingeben: ");\n fgets(text, 100, stdin);\n\n printf("Verschiebung: ");\n scanf("%d", &shift);\n\n verschluessle(text, shift);\n printf("Ergebnis: %s", text);\n\n return 0;\n}\n```\nDie `main`-Funktion sammelt die Eingaben vom Benutzer, ruft die Verschlüsselungsfunktion auf und gibt das Resultat aus. Es ist eine einfache Demonstration der entwickelten Funktionen.'
},
{
id: 'prog-ex6',
title: 'Programmieren: Array Sortieren (Bubble Sort)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void tausche(int *a, int *b)`, die die Werte der beiden Ganzzahlen, auf die `a` und `b` zeigen, vertauscht.',
solution: 'void tausche(int *a, int *b) {\n int temp = *a;\n *a = *b;\n *b = temp;\n}',
points: 6,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void bubble_sort(int arr[], int n)`, die ein Array `arr` der Größe `n` mithilfe des Bubble-Sort-Algorithmus aufsteigend sortiert. Verwenden Sie die `tausche()`-Funktion.',
solution: 'void bubble_sort(int arr[], int n) {\n for (int i = 0; i < n - 1; i++) {\n for (int j = 0; j < n - i - 1; j++) {\n if (arr[j] > arr[j + 1]) {\n tausche(&arr[j], &arr[j + 1]);\n }\n }\n }\n}',
points: 10,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die ein unsortiertes Integer-Array initialisiert, es ausgibt, `bubble_sort()` aufruft und das sortierte Array erneut ausgibt.\n\n**Hinweis:** Um die Anzahl der Elemente in einem Array zu ermitteln, können Sie den Ausdruck `sizeof(arr) / sizeof(arr[0])` verwenden.',
solution: '#include <stdio.h>\n\nvoid tausche(int *a, int *b);\nvoid bubble_sort(int arr[], int n);\nvoid print_array(int arr[], int size);\n\nint main() {\n int arr[] = {64, 34, 25, 12, 22, 11, 90};\n int n = sizeof(arr) / sizeof(arr[0]);\n\n printf("Unsortiertes Array:\\n");\n print_array(arr, n);\n\n bubble_sort(arr, n);\n\n printf("\\nSortiertes Array:\\n");\n print_array(arr, n);\n\n return 0;\n}\n\nvoid print_array(int arr[], int size) {\n for (int i = 0; i < size; i++)\n printf("%d ", arr[i]);\n printf("\\n");\n}',
points: 8,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm implementiert den Bubble-Sort-Algorithmus, einen einfachen, aber lehrreichen Sortieralgorithmus.\n\n**Teil a)**\n```c\nvoid tausche(int *a, int *b) {\n int temp = *a;\n *a = *b;\n *b = temp;\n}\n```\nDie `tausche`-Funktion benötigt Zeiger (`int *`), um die Werte an den ursprünglichen Speicherorten im Array zu ändern. Eine Übergabe per Wert (`int a, int b`) würde nur Kopien der Werte tauschen, was keinen Effekt auf das Array in `main` hätte. Die `temp`-Variable ist notwendig, um einen der Werte zwischenzuspeichern.\n\n**Teil b)**\n```c\nvoid bubble_sort(int arr[], int n) {\n // Äußere Schleife für die Durchläufe\n for (int i = 0; i < n - 1; i++) {\n // Innere Schleife für die Vergleiche\n // n - i - 1, da die letzten i Elemente bereits sortiert sind\n for (int j = 0; j < n - i - 1; j++) {\n // Wenn das aktuelle Element größer als das nächste ist -> tauschen\n if (arr[j] > arr[j + 1]) {\n tausche(&arr[j], &arr[j + 1]);\n }\n }\n }\n}\n```\nBubble Sort vergleicht benachbarte Elemente und tauscht sie, wenn sie in der falschen Reihenfolge sind. Die äußere Schleife sorgt dafür, dass dieser Vorgang oft genug wiederholt wird, bis das gesamte Array sortiert ist. Die innere Schleife "blubbert" das größte unsortierte Element an die richtige Position.\n\n**Teil c)**\n```c\n// includes und Prototypen...\n\nint main() {\n int arr[] = {64, 34, 25, 12, 22, 11, 90};\n // Array-Größe dynamisch berechnen\n int n = sizeof(arr) / sizeof(arr[0]);\n\n printf("Unsortiertes Array: ");\n // (Ausgabefunktion hier)\n\n bubble_sort(arr, n);\n\n printf("Sortiertes Array: ");\n // (Ausgabefunktion hier)\n\n return 0;\n}\n```\nDie `main`-Funktion demonstriert die Nutzung. `sizeof(arr) / sizeof(arr[0])` ist eine robuste Methode, um die Anzahl der Elemente in einem Array zu bestimmen, unabhängig von dessen deklarierter Größe oder Typ.'
},
{
id: 'prog-ex7',
title: 'Programmieren: Dynamischer Speicher',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `int* erstelle_dyn_array(int anzahl)`, die mithilfe von `malloc()` Speicher für ein Array von `anzahl` Integern reserviert. Die Funktion soll den Zeiger auf den Anfang des Speicherbereichs zurückgeben. Geben Sie eine Fehlermeldung aus und geben Sie `NULL` zurück, wenn `malloc` fehlschlägt.',
solution: '#include <stdlib.h>\n#include <stdio.h>\n\nint* erstelle_dyn_array(int anzahl) {\n int* ptr = (int*) malloc(anzahl * sizeof(int));\n if (ptr == NULL) {\n printf("Fehler: Speicher konnte nicht reserviert werden.\\n");\n }\n return ptr;\n}',
points: 8,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `int berechne_summe(int* arr, int anzahl)`, die die Summe aller Elemente in dem übergebenen Array `arr` der Größe `anzahl` berechnet und zurückgibt.',
solution: 'int berechne_summe(int* arr, int anzahl) {\n int summe = 0;\n for (int i = 0; i < anzahl; i++) {\n summe += arr[i];\n }\n return summe;\n}',
points: 6,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion: Fragen Sie den Benutzer nach der Anzahl der zu speichernden Zahlen. Rufen Sie `erstelle_dyn_array()` auf. Füllen Sie das Array mit Zahlen (z.B. `i * 2`). Rufen Sie `berechne_summe()` auf und geben Sie das Ergebnis aus. Geben Sie am Ende den Speicher mit `free()` wieder frei.',
solution: '#include <stdio.h>\n#include <stdlib.h>\n\nint* erstelle_dyn_array(int anzahl);\nint berechne_summe(int* arr, int anzahl);\n\nint main() {\n int anzahl;\n printf("Wie viele Zahlen moechten Sie speichern? ");\n scanf("%d", &anzahl);\n\n int* mein_array = erstelle_dyn_array(anzahl);\n\n if (mein_array == NULL) {\n return 1; // Programm mit Fehlercode beenden\n }\n\n printf("Fuelle Array...\\n");\n for (int i = 0; i < anzahl; i++) {\n mein_array[i] = i * 2;\n }\n\n int summe = berechne_summe(mein_array, anzahl);\n printf("Die Summe der Elemente ist: %d\\n", summe);\n\n free(mein_array);\n mein_array = NULL; // Good practice\n\n return 0;\n}',
points: 10,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm zeigt die Grundlagen der dynamischen Speicherverwaltung in C, ein wesentliches Konzept für flexible Programme.\n\n**Teil a)**\n```c\n#include <stdlib.h> // für malloc\n#include <stdio.h> // für printf\n\nint* erstelle_dyn_array(int anzahl) {\n // Speicher anfordern: anzahl * Größe eines Integers\n int* ptr = (int*) malloc(anzahl * sizeof(int));\n // Prüfen, ob malloc erfolgreich war\n if (ptr == NULL) {\n printf("Fehler: Speicher konnte nicht reserviert werden.\\n");\n }\n return ptr;\n}\n```\n`malloc` (memory allocation) reserviert einen Speicherblock einer bestimmten Größe in Bytes und gibt einen `void`-Zeiger darauf zurück. `anzahl * sizeof(int)` berechnet die benötigte Größe. Der `(int*)`-Cast wandelt den `void*` in den korrekten Zeigertyp um. Es ist essenziell, das Ergebnis von `malloc` auf `NULL` zu prüfen, da die Speicheranforderung fehlschlagen kann.\n\n**Teil b)**\n```c\nint berechne_summe(int* arr, int anzahl) {\n int summe = 0;\n for (int i = 0; i < anzahl; i++) {\n summe += arr[i]; // oder: summe += *(arr + i);\n }\n return summe;\n}\n```\nDiese Funktion ist eine Standard-Array-Verarbeitung. Sie zeigt, dass ein dynamisch alloziertes Array genau wie ein statisches Array mit der `[]`-Notation oder Zeigerarithmetik durchlaufen werden kann.\n\n**Teil c)**\n```c\n// includes und Prototypen...\n\nint main() {\n // ... Benutzereingabe ...\n int* mein_array = erstelle_dyn_array(anzahl);\n\n if (mein_array == NULL) {\n return 1; // Fehlerfall behandeln\n }\n\n // ... Array füllen und Summe berechnen ...\n\n // Speicher freigeben, wenn er nicht mehr benötigt wird\n free(mein_array);\n // Zeiger auf NULL setzen, um "dangling pointer" zu vermeiden\n mein_array = NULL; \n\n return 0;\n}\n```\nDie `main`-Funktion orchestriert den Prozess. Der wichtigste Teil ist der Aufruf von `free(mein_array)` am Ende. `free()` gibt den von `malloc` reservierten Speicher wieder frei. Wird dies vergessen, entsteht ein "Memory Leak" das Programm belegt Speicher, den es nicht mehr freigibt. Den Zeiger danach auf `NULL` zu setzen, ist eine gute Praxis, um zu verhindern, dass man versehentlich auf den bereits freigegebenen Speicher zugreift.'
},
{
id: 'prog-ex8',
title: 'Programmieren: Datei-IO (Punkte-Logger)',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void schreibe_punktzahl(char* dateiname, char* spieler, int punkte)`, die den Namen des Spielers und seine Punktzahl in die angegebene Datei schreibt. Die Einträge sollen angehängt werden, nicht überschrieben. Format: `Spieler: [Name], Punkte: [Punkte]\\n`.\n\n**Hinweis:** Verwenden Sie `fopen(dateiname, "a")` um die Datei zum Anhängen zu öffnen (`a` für append). Schreiben Sie formatiert mit `fprintf()`. Vergessen Sie nicht, die Datei mit `fclose()` zu schließen.',
solution: '#include <stdio.h>\n\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte) {\n FILE *datei = fopen(dateiname, "a"); // "a" für append (anhängen)\n if (datei == NULL) {\n printf("Fehler beim Oeffnen der Datei zum Schreiben.\\n");\n return;\n }\n fprintf(datei, "Spieler: %s, Punkte: %d\\n", spieler, punkte);\n fclose(datei);\n}',
points: 9,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void lese_punktzahlen(char* dateiname)`, die den gesamten Inhalt der angegebenen Datei zeilenweise liest und auf der Konsole ausgibt.\n\n**Hinweis:** Öffnen Sie die Datei mit `fopen(dateiname, "r")` zum Lesen (`r` für read). Prüfen Sie, ob der zurückgegebene `FILE*`-Zeiger `NULL` ist (Fehlerfall). Lesen Sie zeilenweise mit `fgets()` in einer Schleife, bis es `NULL` zurückgibt (Dateiende). Schließen Sie die Datei am Ende mit `fclose()`.',
solution: '#include <stdio.h>\n\nvoid lese_punktzahlen(char* dateiname) {\n char zeile[100];\n FILE *datei = fopen(dateiname, "r"); // "r" für read (lesen)\n if (datei == NULL) {\n printf("Datei nicht gefunden oder leer.\\n");\n return;\n }\n printf("\\n--- Highscores ---\\n");\n while (fgets(zeile, sizeof(zeile), datei) != NULL) {\n printf("%s", zeile);\n }\n printf("------------------\\n");\n fclose(datei);\n}',
points: 8,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die `schreibe_punktzahl()` für zwei fiktive Spieler aufruft (z.B. "Alice" mit 150 Pkt, "Bob" mit 120 Pkt). Rufen Sie anschließend `lese_punktzahlen()` auf, um das Ergebnis zu überprüfen.',
solution: '#include <stdio.h>\n\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte);\nvoid lese_punktzahlen(char* dateiname);\n\nint main() {\n char* dateiname = "scores.txt";\n\n // Alte Datei ggf. löschen für einen sauberen Testlauf\n remove(dateiname);\n\n printf("Schreibe Punktzahlen in die Datei...\\n");\n schreibe_punktzahl(dateiname, "Alice", 150);\n schreibe_punktzahl(dateiname, "Bob", 120);\n\n lese_punktzahlen(dateiname);\n\n return 0;\n}',
points: 7,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDieses Programm demonstriert die grundlegende Dateiein- und -ausgabe (File I/O) in C, um Daten persistent zu speichern.\n\n**Teil a)**\n```c\nvoid schreibe_punktzahl(char* dateiname, char* spieler, int punkte) {\n // Datei im "append"-Modus öffnen. Erstellt die Datei, wenn sie nicht existiert.\n FILE *datei = fopen(dateiname, "a");\n if (datei == NULL) {\n // Fehlerbehandlung ist wichtig!\n printf("Fehler beim Oeffnen der Datei zum Schreiben.\\n");\n return;\n }\n // fprintf funktioniert wie printf, schreibt aber in eine Datei.\n fprintf(datei, "Spieler: %s, Punkte: %d\\n", spieler, punkte);\n // Datei schließen, um Änderungen zu speichern und Ressourcen freizugeben.\n fclose(datei);\n}\n```\n`fopen` öffnet eine Datei und gibt einen `FILE`-Zeiger zurück. Der Modus `"a"` (append) ist entscheidend, um neue Einträge am Ende der Datei hinzuzufügen, anstatt sie zu überschreiben (Modus `"w"`). `fprintf` wird für formatiertes Schreiben in die Datei verwendet. `fclose` ist obligatorisch.\n\n**Teil b)**\n```c\nvoid lese_punktzahlen(char* dateiname) {\n char zeile[100];\n // Datei im "read"-Modus öffnen.\n FILE *datei = fopen(dateiname, "r"); \n if (datei == NULL) {\n printf("Datei nicht gefunden oder leer.\\n");\n return;\n }\n printf("\\n--- Highscores ---\\n");\n // Lese zeilenweise, bis das Ende der Datei erreicht ist (fgets gibt NULL zurück).\n while (fgets(zeile, sizeof(zeile), datei) != NULL) {\n printf("%s", zeile);\n }\n printf("------------------\\n");\n fclose(datei);\n}\n```\nHier wird `fopen` im Modus `"r"` (read) verwendet. `fgets` ist eine sichere Methode, um zeilenweise aus einer Datei zu lesen, da es die Puffergröße beachtet und so Buffer Overflows verhindert. Die Schleife liest so lange, bis `fgets` das Dateiende erreicht und `NULL` zurückgibt.\n\n**Teil c)**\n```c\nint main() {\n char* dateiname = "scores.txt";\n\n // Optional: remove() löscht eine Datei. Nützlich für wiederholbare Tests.\n remove(dateiname);\n\n schreibe_punktzahl(dateiname, "Alice", 150);\n schreibe_punktzahl(dateiname, "Bob", 120);\n\n lese_punktzahlen(dateiname);\n\n return 0;\n}\n```\nDie `main`-Funktion dient als Treiber, um die beiden I/O-Funktionen zu testen und deren Zusammenspiel zu demonstrieren.'
},
{
id: 'prog-ex9',
title: 'Programmieren: Rechteck-Struktur & Funktionen',
parts: [
{
id: 'a',
prompt: 'Definieren Sie mittels `typedef` einen Datentyp `RECHTECK` für eine Struktur, die `breite` und `hoehe` (beides `float`) enthält.',
solution: 'typedef struct {\n float breite;\n float hoehe;\n} RECHTECK;',
points: 6,
type: 'typedef-struct'
},
{
id: 'b',
prompt: 'Erstellen Sie eine Funktion `float berechne_flaeche(RECHTECK *r)`, die die Fläche eines Rechtecks berechnet. Die Funktion soll einen Zeiger auf eine `RECHTECK`-Struktur als Parameter erhalten.',
solution: 'float berechne_flaeche(RECHTECK *r) {\n return r->breite * r->hoehe;\n}',
points: 9,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Schreiben Sie eine `main`-Funktion, die eine `RECHTECK`-Variable deklariert und initialisiert. Rufen Sie dann `berechne_flaeche` auf, um die Fläche zu berechnen und geben Sie das Ergebnis auf der Konsole aus.',
solution: '#include <stdio.h>\n\ntypedef struct {\n float breite;\n float hoehe;\n} RECHTECK;\n\nfloat berechne_flaeche(RECHTECK *r);\n\nint main() {\n RECHTECK mein_rechteck = {10.5, 5.2};\n\n float flaeche = berechne_flaeche(&mein_rechteck);\n\n printf("Das Rechteck mit Breite %.2f und Hoehe %.2f hat eine Flaeche von %.2f.\\n", mein_rechteck.breite, mein_rechteck.hoehe, flaeche);\n\n return 0;\n}',
points: 9,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDiese Aufgabe verdeutlicht das Zusammenspiel von Strukturen (`struct`) und Funktionen, insbesondere die effiziente Übergabe von Strukturen mittels Zeigern.\n\n**Teil a)**\n```c\ntypedef struct {\n float breite;\n float hoehe;\n} RECHTECK;\n```\nEin `typedef` wird verwendet, um einen Alias `RECHTECK` für die anonyme Struktur zu erstellen. Dies macht den Code lesbarer, da man nicht immer `struct ...` schreiben muss.\n\n**Teil b)**\n```c\nfloat berechne_flaeche(RECHTECK *r) {\n return r->breite * r->hoehe;\n}\n```\nDiese Funktion nimmt einen Zeiger auf ein `RECHTECK` entgegen (`RECHTECK *r`). Dies ist weitaus effizienter als die Übergabe der ganzen Struktur per Wert (`RECHTECK r`), besonders bei großen Strukturen. Bei der Übergabe per Zeiger wird nur die Speicheradresse (typ. 4 oder 8 Bytes) kopiert, nicht die gesamte Struktur. Der `->`-Operator (Pfeiloperator) wird verwendet, um auf Strukturelemente über einen Zeiger zuzugreifen. `r->breite` ist eine Kurzschreibweise für `(*r).breite`.\n\n**Teil c)**\n```c\nint main() {\n RECHTECK mein_rechteck = {10.5, 5.2};\n\n // Die Adresse der Struktur wird mit dem &-Operator übergeben.\n float flaeche = berechne_flaeche(&mein_rechteck);\n\n printf("Die Flaeche betraegt: %.2f\\n", flaeche);\n\n return 0;\n}\n```\nIn `main` wird eine Instanz der Struktur erstellt und initialisiert. Beim Aufruf von `berechne_flaeche` wird der Adressoperator `&` verwendet, um einen Zeiger auf `mein_rechteck` zu erzeugen, der dann an die Funktion übergeben wird. Dies entspricht dem von der Funktion erwarteten Parametertyp `RECHTECK*`.'
},
{
id: 'prog-ex10',
title: 'Programmieren: Matrix-Transponierung',
parts: [
{
id: 'a',
prompt: 'Erstellen Sie eine Funktion `void print_matrix(int matrix[3][3])`, die eine 3x3-Matrix formatiert auf der Konsole ausgibt.',
solution: '#include <stdio.h>\n\nvoid print_matrix(int matrix[3][3]) {\n for (int i = 0; i < 3; i++) {\n for (int j = 0; j < 3; j++) {\n printf("%d\\t", matrix[i][j]);\n }\n printf("\\n");\n }\n}',
points: 7,
type: 'function-definition'
},
{
id: 'b',
prompt: 'Schreiben Sie eine Funktion `void transponiere_matrix(int matrix[3][3])`, die die übergebene 3x3-Matrix an Ort und Stelle ("in-place") transponiert. Dabei werden die Zeilen und Spalten vertauscht (Element an `(i,j)` wird mit Element an `(j,i)` getauscht).',
solution: 'void transponiere_matrix(int matrix[3][3]) {\n int temp;\n for (int i = 0; i < 3; i++) {\n for (int j = i + 1; j < 3; j++) {\n temp = matrix[i][j];\n matrix[i][j] = matrix[j][i];\n matrix[j][i] = temp;\n }\n }\n}',
points: 9,
type: 'function-definition'
},
{
id: 'c',
prompt: 'Erstellen Sie eine `main()`-Funktion, die eine 3x3-Matrix initialisiert. Geben Sie die Originalmatrix mit `print_matrix()` aus, rufen Sie `transponiere_matrix()` auf und geben Sie die transponierte Matrix erneut aus.',
solution: '#include <stdio.h>\n\nvoid print_matrix(int matrix[3][3]);\nvoid transponiere_matrix(int matrix[3][3]);\n\nint main() {\n int matrix[3][3] = {\n {1, 2, 3},\n {4, 5, 6},\n {7, 8, 9}\n };\n\n printf("Originalmatrix:\\n");\n print_matrix(matrix);\n\n transponiere_matrix(matrix);\n\n printf("\\nTransponierte Matrix:\\n");\n print_matrix(matrix);\n\n return 0;\n}',
points: 8,
type: 'main-function'
}
],
explanation: '### Lösungserklärung\n\nDiese Übung konzentriert sich auf den Umgang mit zweidimensionalen Arrays (Matrizen) und die Implementierung eines Matrix-Algorithmus.\n\n**Teil a)**\n```c\nvoid print_matrix(int matrix[3][3]) {\n // Äußere Schleife für die Zeilen\n for (int i = 0; i < 3; i++) {\n // Innere Schleife für die Spalten\n for (int j = 0; j < 3; j++) {\n printf("%d\\t", matrix[i][j]); // \\t für Tabulator -> saubere Spalten\n }\n printf("\\n"); // Neue Zeile nach jeder Matrix-Zeile\n }\n}\n```\nDas Ausgeben einer Matrix erfordert zwei verschachtelte Schleifen. Die äußere iteriert über die Zeilen (erster Index), die innere über die Spalten (zweiter Index). Der Zugriff auf ein Element erfolgt mit `matrix[zeile][spalte]`.\n\n**Teil b)**\n```c\nvoid transponiere_matrix(int matrix[3][3]) {\n int temp;\n // Iteriere über das obere Dreieck der Matrix\n for (int i = 0; i < 3; i++) {\n for (int j = i + 1; j < 3; j++) {\n // Tausche element(i,j) mit element(j,i)\n temp = matrix[i][j];\n matrix[i][j] = matrix[j][i];\n matrix[j][i] = temp;\n }\n }\n}\n```\nBeim Transponieren werden die Elemente über die Hauptdiagonale gespiegelt. Um dies "in-place" zu tun, müssen wir nur die Elemente im oberen (oder unteren) Dreieck der Matrix durchlaufen und sie mit ihrem Gegenstück tauschen. Die innere Schleife startet bei `j = i + 1`, um zu vermeiden, dass Elemente auf der Diagonale mit sich selbst und andere Elemente doppelt getauscht werden (was die Transponierung rückgängig machen würde).\n\n**Teil c)**\n```c\nint main() {\n int matrix[3][3] = {\n {1, 2, 3},\n {4, 5, 6},\n {7, 8, 9}\n };\n\n printf("Originalmatrix:\\n");\n print_matrix(matrix);\n\n transponiere_matrix(matrix);\n\n printf("\\nTransponierte Matrix:\\n");\n print_matrix(matrix);\n\n return 0;\n}\n```\nDie `main`-Funktion initialisiert eine Beispielmatrix und nutzt die zuvor erstellten Funktionen, um den Transponierungs-Vorgang zu demonstrieren und das Ergebnis sichtbar zu machen. Da Arrays in C als Referenz übergeben werden, modifiziert `transponiere_matrix` die Originalmatrix in `main` direkt.'
}
];

54
index.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>C Struct Practice</title>
<script src="https://cdn.tailwindcss.com?plugins=typography"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@400;700&family=Roboto:wght@400;500;700&family=Roboto+Mono:wght@400;500&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" />
<style>
.code-editor-textarea::placeholder {
color: #64748b; /* slate-500 */
opacity: 1; /* Firefox */
-webkit-text-fill-color: #64748b;
}
</style>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Roboto', 'sans-serif'],
serif: ['Roboto Slab', 'serif'],
mono: ['Roboto Mono', 'monospace'],
},
},
},
}
</script>
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@^19.1.0",
"react/": "https://esm.sh/react@^19.1.0/",
"react-dom/": "https://esm.sh/react-dom@^19.1.0/",
"@google/genai": "https://esm.sh/@google/genai",
"react-markdown": "https://esm.sh/react-markdown@9?bundle",
"remark-gfm": "https://esm.sh/remark-gfm@4?bundle",
"diff": "https://esm.sh/diff@5?bundle",
"react-simple-code-editor": "https://esm.sh/react-simple-code-editor@0.13.1",
"prismjs/": "https://esm.sh/prismjs@1.29.0/"
}
}
</script>
<link rel="stylesheet" href="/index.css">
</head>
<body class="bg-slate-100">
<div id="root"></div>
<script type="module" src="/index.tsx"></script>
</body>
</html>

16
index.tsx Normal file
View File

@ -0,0 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const rootElement = document.getElementById('root');
if (!rootElement) {
throw new Error("Could not find root element to mount to");
}
const root = ReactDOM.createRoot(rootElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

6
metadata.json Normal file
View File

@ -0,0 +1,6 @@
{
"name": "C Struct Practice",
"description": "An interactive web application to practice C language data structures (structs) through a series of guided exercises. Users can write code, submit it for validation, and receive detailed feedback and solutions.",
"requestFramePermissions": [],
"prompt": ""
}

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "c-struct-practice",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@google/genai": "latest",
"@headlessui/react": "^2.2.4",
"@heroicons/react": "^2.2.0",
"diff": "5.2.0",
"prismjs": "1.29.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-markdown": "9.1.0",
"react-simple-code-editor": "0.13.1",
"remark-gfm": "4.0.1"
},
"devDependencies": {
"@types/diff": "^8.0.0",
"@types/node": "^22.14.0",
"@types/prismjs": "^1.26.5",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"typescript": "~5.7.2",
"vite": "^6.2.0"
}
}

2047
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
ignoredBuiltDependencies:
- esbuild

30
tsconfig.json Normal file
View File

@ -0,0 +1,30 @@
{
"compilerOptions": {
"target": "ES2020",
"experimentalDecorators": true,
"useDefineForClassFields": false,
"module": "ESNext",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"isolatedModules": true,
"moduleDetection": "force",
"noEmit": true,
"allowJs": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
"paths": {
"@/*" : ["./*"]
}
}
}

23
types.ts Normal file
View File

@ -0,0 +1,23 @@
export type ExercisePartType = 'struct-definition' | 'typedef-struct' | 'array-initialization' | 'pointer-manipulation' | 'printf' | 'function-definition' | 'main-function';
export interface ExercisePart {
id: string;
prompt: string;
solution: string;
points: number;
type: ExercisePartType;
}
export interface Exercise {
id: string;
title: string;
parts: ExercisePart[];
explanation: string;
}
export interface CheckResult {
partId: string;
isCorrect: boolean;
explanation: string;
error?: string; // To hold API or parsing errors
}

17
vite.config.ts Normal file
View File

@ -0,0 +1,17 @@
import path from 'path';
import { defineConfig, loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, '.', '');
return {
define: {
'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
},
resolve: {
alias: {
'@': path.resolve(__dirname, '.'),
}
}
};
});