CS_LEVEL_DESIGNER_GUIDE - OnlyCook/abitur-elite-code GitHub Wiki

Level Designer Dokumentation

Willkommen im Level Designer von Abitur Elite Code. Dieses Werkzeug ermöglicht es dir, eigene Programmieraufgaben im Stil der hessischen Abiturprüfung (Praktische Informatik) zu erstellen, zu testen und mit anderen zu teilen.

Inhaltsverzeichnis

  1. Dateisystem & Speicherort
  2. Die Benutzeroberfläche
  3. Code Editoren
  4. Leitfaden für Abitur-konforme Levels
  5. Schritt-für-Schritt: Dein erstes Level

Dateisystem & Speicherort

Bevor wir beginnen, ist es wichtig, den Unterschied zwischen den zwei Dateitypen zu verstehen:

  • .elitelvldraft: Dies sind Entwürfe. Sie können jederzeit im Designer bearbeitet werden, sind aber nicht "spielbar" (man kann sie nicht im normalen Menü auswählen, um sie zu lösen).
  • .elitelvl: Dies sind exportierte Levels. Sie sind schreibgeschützt und können von Nutzern gespielt werden. Ein Export ist nur möglich, wenn der Entwurf den internen Selbsttest im Designer besteht.

Speicherort:
Alle Levels befinden sich im Ordner levels im Verzeichnis der Anwendung.
Um ein Level zu teilen, sende einfach die .elitelvl-Datei an eine andere Person. Diese muss die Datei dann in ihren levels-Ordner legen.

Die Benutzeroberfläche

Metadaten

  • Level Name: Der Titel des Levels, der in der Auswahl und im Header angezeigt wird.
  • Autor: Dein Name oder Pseudonym.

Das Textfeld "Aufgabe"

Hier beschreibst du die Aufgabenstellung. Du kannst spezielle Formatierungen verwenden, um den Text übersichtlicher zu gestalten:

  • Fetter Text: Umschließe Text mit zwei Sternchen.

    • Code: **Wichtig**
    • Ergebnis: Wichtig
  • Highlight (Blau): Umschließe Begriffe mit eckigen Klammern. Ideal für Klassennamen oder Methoden.

    • Code: Implementiere die Klasse [Tier].
    • Ergebnis: Implementiere die Klasse Tier (wird in der App blau dargestellt).
  • Code-Block: Umschließe Code mit {| und |}.

    • Code: {|public void Run()|}
    • Ergebnis:
public void Run()

Das Textfeld "Materialien"

Hier kommen Hinweise, Tipps oder weiterführende Erklärungen hin. Auch hier funktioniert die Formatierung wie bei der Aufgabe. Zusätzlich gibt es das Hinweis-System:

Du kannst einklappbare Hinweise erstellen, um Lösungen nicht sofort zu verraten.

Syntax:

start-hint: Titel des Hinweises
Hier steht der Text, der erst nach dem Ausklappen sichtbar ist.
Du kannst auch [Code] verwenden.
:end-hint

Für Tipps verwende start-tipp statt start-hint:

start-tipp: Ein kleiner Tipp
Verwende eine for-Schleife.
:end-tipp

UML / Diagramme (PlantUML)

Jedes Level benötigt in der Regel ein Klassendiagramm. Abitur Elite Code nutzt PlantUML zur Generierung.

  • Der Designer fügt automatisch ein Theme hinzu (skinparam backgroundcolor transparent etc.). Du musst dich nicht um das Styling kümmern.
  • Du kannst mehrere Reiter für Diagramme anlegen (z.B. Hauptdiagramm und Hilfsdiagramme für Materialien).
  • Klicke auf den "Vorschau generieren"-Button (das Zauberstab-Icon), um das Diagramm zu aktualisieren.

Dokumentation zu PlantUML:
Offizielle PlantUML Referenz
Offizielle PlantUML Klassendiagramme Referenz

Voraussetzungen (Prerequisites)

Du kannst bis zu 8 Voraussetzungen definieren. Diese werden dem Nutzer angezeigt und verlinken auf passende Lernressourcen (Dometrain Kurse und Microsoft Docs).

Liste aller verfügbaren Voraussetzungen (Klicken zum Ausklappen)

Die Liste orientiert sich am Curriculum des kostenlosen Kurses "Hands-on C# for Beginners" auf Dometrain.

Hello World & Basics

  • Console printing
  • Console.Write
  • Console.ReadLine
  • Single line comments
  • Multi line comments
  • Variables
  • Constants
  • The var keyword

Primitive Types

  • Integers
  • Doubles
  • Decimals
  • Strings
  • Escape Sequences
  • Verbatim Strings
  • String concatenation
  • String interpolation
  • Chars
  • Booleans
  • Floats

Operators

  • Addition
  • Subtraction
  • Multiplication
  • Division
  • The Modulo operator
  • Order of operations
  • Compound assignment operators
  • Increment and Decrement
  • Comparison operators
  • Logical AND
  • Logical OR
  • Logical NOT

Control Flow

  • If statements
  • If-Else statements
  • Else-If chains
  • Logical patterns
  • Switch statements
  • Switch expressions
  • The Ternary operator

Loops

  • While Loops
  • Avoiding Infinite Loops
  • Do-While Loops
  • For Loops
  • For Loop Counting Down
  • For-Each Loops
  • The break statement
  • The continue statement
  • Nested Loops

Methods

  • Defining void methods
  • Method parameters
  • Return values
  • Using return values
  • Method overloading
  • Optional parameters
  • Named arguments
  • Params arguments
  • ref Parameters
  • out Parameters
  • in Parameters

Arrays

  • Creating Arrays
  • Array Initializer Syntax
  • Modifying Array Elements
  • Looping Arrays with for
  • Looping Arrays with foreach
  • Multi-Dimensional Arrays
  • Jagged Arrays
  • Ranges and Indices

Collections (Lists & Dictionaries)

  • Creating Lists
  • Adding to Lists
  • Accessing List Elements
  • Removing from Lists
  • Checking List Contents
  • Sorting Lists
  • Creating Dictionaries
  • Adding and Accessing Dictionary Items
  • Checking Dictionary Keys
  • Looping Through Dictionaries

OOP: Classes & Objects

  • Defining a Class
  • Fields
  • Default Constructors
  • Parameterized Constructors
  • Constructor Overloading
  • Properties
  • Auto-Properties
  • Read-Only Properties
  • Private Set Properties
  • The this Keyword
  • Public Access Modifier
  • Private Access Modifier

OOP: Advanced

  • The static keyword
  • Static Fields
  • Static Methods
  • Inheritance Basics
  • The base Keyword
  • Virtual Methods
  • Method Overriding
  • Abstract Classes
  • Abstract Methods
  • Defining Interfaces
  • Implementing Interfaces
  • Multiple Interfaces
  • Default interface methods

Structs, Records, Enums

  • Defining Structs
  • Value Type Behavior
  • Defining Enums
  • Enum Values
  • Enums in Switch
  • Defining Records
  • Record With Expressions

Exception Handling

  • Try-Catch Blocks
  • Exception Messages
  • Multiple Catch Blocks
  • The Finally Block
  • Throwing Exceptions
  • Custom Exception Messages

Generics

  • Generic Classes
  • Using Generic Classes
  • Generic Methods
  • Generic Constraints

Delegates, Lambdas, Events

  • Defining Delegates
  • Using Delegates
  • Action Delegates
  • Func Delegates
  • Lambda Expression Basics
  • Lambda with Multiple Statements
  • Events
  • Subscribing to Events

LINQ

  • Introduction to LINQ
  • LINQ Query Syntax
  • Where for Filtering
  • Select for Transforming
  • OrderBy for Sorting
  • ThenBy for Secondary Sorting
  • Count and Sum
  • Average, Min, and Max
  • First and FirstOrDefault
  • Single and SingleOrDefault
  • Any and All

Async Programming

  • Async and Await
  • Returning Values from Async
  • Task.WhenAll
  • Task.WhenAny

Strings & DateTime

  • ToUpper and ToLower
  • Trim
  • Substring
  • Replace
  • Split
  • Join
  • Contains and IndexOf
  • StartsWith and EndsWith
  • String Comparisons
  • Format Specifiers
  • Interpolation Format
  • StringBuilder
  • DateTime Basics
  • Creating DateTime Values
  • Formatting Dates
  • Parsing Dates
  • Date Arithmetic
  • TimeSpan Basics
  • Comparing Dates
  • DateOnly Basics
  • TimeOnly Basics

Advanced / Modern C#

  • Nullable Value Types
  • The Null-Coalescing Operator
  • Nullable Reference Types
  • The Null-Conditional Operator
  • The Null-Forgiving Operator
  • Type Checking with is
  • Type Patterns with Variables
  • Switch with Type Patterns
  • Property Patterns
  • Relational Patterns
  • When Guards
  • Creating Tuples
  • Named Tuple Elements
  • Returning Tuples from Methods
  • Tuple Deconstruction
  • Anonymous Types
  • Extension Methods
  • Understanding Attributes
  • Creating Custom Attributes
  • The using Statement
  • Init-Only Properties
  • Required Properties
  • Raw String Literals
  • Collection Expression Syntax
  • Spread Operator in Collections
  • Primary Constructors

Conversion & I/O

  • Implicit Conversion
  • Explicit Conversion (Casting)
  • The Convert Class
  • Parse Methods
  • TryParse Methods
  • Checked Arithmetic
  • Unchecked Arithmetic
  • Writing Text Files
  • Reading Text Files
  • File Lines
  • Checking File Existence
  • Path Manipulation
  • JSON Serialization
  • JSON Deserialization

HTTP Requests

  • Introduction to HttpClient
  • Making GET Requests
  • HttpResponseMessage
  • Checking Status Codes
  • Using BaseAddress
  • Making POST Requests
  • Sending JSON with POST
  • Deserializing JSON Responses
  • Setting Request Headers
  • Handling HTTP Errors
  • PUT and DELETE Requests
  • Async HTTP Operations

C# 13 & 14 Preview

  • Params Collections
  • The Lock Type
  • Partial Properties
  • The field Keyword

Code Editoren

Der Designer verfügt über drei Code-Fenster. Um diese zu bearbeiten, klicke auf das Pfeil-Icon neben der jeweiligen Überschrift.

1. Starter Code

Dies ist der Code, den der Spieler zu Beginn des Levels sieht.

  • Er sollte das Grundgerüst enthalten (Klassenrümpfe, Methodensignaturen).
  • Vermeide es, Lösungen hier schon vorzugeben.

2. Musterlösung / Test-Code

Dieser Code wird niemals exportiert. Er ist nur für dich während der Entwicklung im Designer sichtbar.

  • Nutze dieses Feld, um deine Validierungslogik zu testen.
  • Schreibe hier eine korrekte Lösung für deine Aufgabe rein.
  • Wenn du im Designer auf "Ausführen" klickst, wird dieser Code gegen deine Validierung geprüft.

3. Validierungs-Code (Roslyn)

Dies ist das Herzstück deines Levels. Hier schreibst du C#-Code, der den Code des Spielers analysiert und bewertet.
Die Validierung basiert auf Reflection. Du erhältst das kompilierte Assembly des Spielers und musst prüfen, ob Klassen, Methoden und Rückgabewerte korrekt sind.

Signatur:

private static bool ValidateLevel(Assembly assembly, out string feedback)
{
    // Deine Logik
}

Rückgabewerte:

  • true: Das Level gilt als bestanden.
  • false: Das Level ist nicht bestanden. Das Programm wirft dann meistens eine Exception mit Details.
  • feedback: Eine Nachricht, die dem Nutzer bei Erfolg angezeigt wird.

Wichtiges Konzept:
Da der Code des Spielers erst zur Laufzeit existiert, kannst du nicht einfach new Tier() schreiben (das würde einen Compiler-Fehler geben, da dein Validator die Klasse Tier nicht kennt). Du musst assembly.GetType("Tier") verwenden.

Leitfaden für Abitur-konforme Levels

Um Levels zu erstellen, die dem Stil des hessischen Abiturs (Praktische Informatik) entsprechen, beachte bitte folgende Regeln:

  1. Java-Notation in Diagrammen: Das Abitur verwendet in Diagrammen Java-Syntax (z.B. String statt string, boolean statt bool, list.add statt list.Add). Deine Aufgabe ist es oft, diese Unterschiede in den "Hinweisen" zu erklären, aber im Diagramm beim Java-Stil zu bleiben.

  2. Namenskonventionen:

    • Java-Diagramm: getGewicht()
    • Erwarteter C#-Code: GetGewicht() (PascalCase für Methoden).
  3. Progression:

    • Levels sollten ein Thema schrittweise einführen.
    • Verwende am Anfang viel "Händchenhalten" (genaue Anweisungen, viele Hinweise).
    • Gegen Ende einer Sektion (Mini-Exam) sollten weniger Hilfen gegeben werden.
  4. Datenkapselung: Getter und Setter werden im Abitur meist explizit gefordert (getVariable()), statt C#-Properties ({ get; set; }) zu nutzen.

  5. Listen: Wenn List<T> verwendet wird, muss dies oft als Assoziation im Diagramm dargestellt werden (mit Sternchen * für "viele").

Schritt-für-Schritt: Dein erstes Level

Hier erstellen wir gemeinsam ein einfaches Level: "Der Taschenrechner".
Folge diesen Schritten und kopiere den Code exakt in die entsprechenden Felder im Designer, um ein funktionierendes, exportierbares Level zu erhalten.

Schritt 1: Metadaten

Klicke im Level-Auswählen-Menü "Eigene Levels" auf + und gib folgende Daten ein:

  • Name: Der einfache Rechner
  • Autor: (Dein Name)

Schritt 2: Aufgabe

Gehe in den Level Designer des neu erzeugte Level-Entwurfs "Der einfache Rechner (Entwurf)" mit .
Kopiere dann folgenden Text in das Feld Aufgabe:

Erstelle eine Klasse [Rechner], die grundlegende mathematische Operationen durchführen kann.

Implementiere die Methode [Addiere(int a, int b)], die die Summe der beiden Zahlen zurückgibt.

Schritt 3: Materialien

Kopiere diesen Text in das Feld Materialien:

start-hint: Wie addiert man?
Nutze den [+] Operator.
Beispiel: {|return a + b;|}
:end-hint

Schritt 4: UML / Diagramm

Füge diesen Code in das Feld Haupt bei "UML/Diagramme (PlantUML)" ein und klicke auf den Vorschau-Button (das kleine Zauberstab-Icon über dem Textfeld):

@startuml
class Rechner {
  + addiere(a : int, b : int) : int
}
@enduml

Schritt 5: Starter Code

Klicke auf den Pfeil bei Starter Code (dies öffnet den Editor). Füge dort diesen Code ein, damit der Spieler nicht bei Null anfangen muss:

public class Rechner
{
    // Hier Code implementieren
}

Schritt 6: Musterlösung / Test-Code

Klicke auf den Pfeil bei Musterlösung / Test-Code. Füge hier die korrekte Lösung ein. Dieser Code wird gleich verwendet, um zu testen, ob deine Validierung funktioniert:

public class Rechner
{
    public int Addiere(int a, int b)
    {
        return a + b;
    }
}

Schritt 7: Validierungs-Code

Das ist der wichtigste Teil. Klicke auf den Pfeil bei Validierungs-Code. Hier schreiben wir das Programm, das die Lösung des Spielers prüft.

Kopiere diesen kompletten Block:

private static bool ValidateLevel(Assembly assembly, out string feedback)
{
    // 1. Suche nach der Klasse "Rechner"
    Type t = assembly.GetType("Rechner");
    if (t == null) 
    {
        throw new Exception("Die Klasse 'Rechner' wurde nicht gefunden. Hast du den Namen korrekt geschrieben?");
    }

    // 2. Erstelle eine Instanz der Klasse (ruft den Konstruktor auf)
    object instance = Activator.CreateInstance(t);

    // 3. Suche nach der Methode "Addiere"
    MethodInfo m = t.GetMethod("Addiere");
    if (m == null) 
    {
        throw new Exception("Die Methode 'Addiere' fehlt in der Klasse Rechner.");
    }

    // 4. Teste die Methode mit Werten (5 + 5)
    // Invoke ruft die Methode auf der Instanz auf. Das Array sind die Parameter (a, b).
    object resultObj = m.Invoke(instance, new object[] { 5, 5 });
    
    // Prüfe das Ergebnis
    if (resultObj is int result)
    {
        if (result == 10)
        {
            feedback = "Super! 5 + 5 ist korrekt 10.";
            return true; // Test bestanden
        }
        else
        {
            throw new Exception($"Falsches Ergebnis. Erwartet: 10, Erhalten: {result}");
        }
    }
    
    throw new Exception("Die Methode gibt kein 'int' zurück.");
}

Schritt 8: Testen & Exportieren

  1. Gehe sicher, dass du im Musterlösung / Test-Code Fenster (Schritt 6) bist oder es zumindest befüllt hast.
  2. Klicke auf den großen grünen ▶ AUSFÜHREN Button oben rechts.
  3. Die Konsole sollte jetzt grün anzeigen: ✓ DESIGNER TEST BESTANDEN.
  4. Der Button Exportieren (grün, oben links im Designer-Tab) ist jetzt aktiv. Klicke darauf.
  5. Fertig! Du hast eine .elitelvl Datei im levels-Ordner erstellt, die du jetzt verschicken kannst.

Beispiel-Dateien herunterladen

Falls du die fertigen Beispiel-Dateien direkt verwenden möchtest:

Lege die heruntergeladenen Dateien einfach in deinen levels-Ordner (neben der .exe, wenn keiner existiert erstelle diesen selbst).

⚠️ **GitHub.com Fallback** ⚠️